# A Dev Server and Build with Vite

Vite gives a web project a dev server that shows every change the moment you save, and a build that bundles your code and packages into a small dist folder ready to put online.

Journeyman · JavaScript · Node.js · Software Development · Updated November 1, 2026

## After this lesson you can

- Create a Vite project and run its dev server with npm create vite and npm run dev
- Build a project with npm run build and explain what the bundler puts in the dist folder

## Key takeaways

- npm create vite@latest sets up a new project. npm install, then npm run dev, starts the dev server at an address like http://localhost:5173.
- The dev server updates the page in the browser each time you save a file. Ctrl+C in the terminal stops it.
- A bundler follows every import from index.html, then combines, shrinks and renames the files so the browser downloads as little as possible.
- npm run build writes the finished site to the dist folder. That folder is what you put online; npm run preview shows it first.

Live Preview has served your pages so far, and for a page with a few files it's all you need. Professional projects are different: they import packages from npm, they're split into dozens of modules, and one day they go online, where every extra kilobyte makes a phone wait. Most new web projects, including React, Vue and Svelte apps, start with the same tool for this: **Vite**. You'll use it any time a class, a tutorial or a job says "create a new project", and it's how your projects in this course get ready to share with a link.

## Two jobs: the mirror and the suitcase

Getting ready for a trip has two parts. First you try things on in front of a mirror, and you see every change the moment you make it. Then you pack the suitcase: everything folded small, nothing you won't need, ready to travel.

A web project needs both:

- A **dev server** (*development server*) is the mirror. It's a small web server on your own computer that shows the page while you work on it and updates the page every time you save a file.
- A **build** is the suitcase. It packs your code and the packages it uses into a few small files, ready to put online.

**Vite** (a French word for "quick", said "veet") does both. It's an npm package, so it lives in the project's `devDependencies`, like the tools in *npm and Packages*.

## Create a project

`npm create vite@latest` sets up a new Vite project. Give it the project's name and a template; `vanilla` means plain JavaScript, with no framework. In the terminal, in your course folder:

```bash
npm create vite@latest game-night-scoreboard -- --template vanilla
```

The `--` on its own (read "dash dash") passes everything after it to Vite's setup program instead of to npm. If npm asks *Ok to proceed?*, press Enter: it's downloading that setup program. If it asks *Install with npm and start now?*, choose **No** for now, so you can see each step. It ends with:

```text
└  Done. Now run:

  cd game-night-scoreboard
  npm install
  npm run dev
```

Open the new `game-night-scoreboard` folder in VS Code. Vite made a working demo page and this `package.json`:

```json
{
  "name": "game-night-scoreboard",
  "version": "0.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "devDependencies": {
    "vite": "^8.3.5"
  }
}
```

Three npm scripts, one for each job: `dev` for the mirror, `build` for the suitcase, and `preview` to check the packed suitcase before it travels.

## The dev server: npm run dev

Run `npm install` to install Vite, then start the dev server:

```bash
npm run dev
```

```text
> game-night-scoreboard@0.0.0 dev
> vite


  VITE v8.3.5  ready in 167 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
```

Ctrl+click the address (Cmd+click on a Mac) to open it. **localhost** means "this computer", and **5173** is the *port*, a numbered door the server listens at, so several servers can run at once. Only you can see this page: the server runs on your computer, for you.

Now change the words in `src/main.js` and save. The browser updates by itself, with no refresh button: Vite watched the file, saw it change and sent the change to the page. A changed style swaps in without even reloading. (Vite calls this **HMR**, *hot module replacement*.) The server keeps running while you work; press **Ctrl+C** in the terminal to stop it.

## The scoreboard on a page

Time to replace the demo with the Game Night Scoreboard. Delete `src/counter.js`, the `src/assets` folder and the `public` folder (they belong to the demo), put a few lines of your own in `src/style.css`, and copy `scores.js` from *npm and Packages* into `src`. Then install a package that celebrates the winner:

```bash
npm install canvas-confetti
```

Replace `index.html` with the page, a heading, an empty list and a button:

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Game Night Scoreboard</title>
  </head>
  <body>
    <h1>Game Night Scoreboard</h1>
    <ol id="standings"></ol>
    <button id="crown" type="button">Crown the winner</button>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>
```

And replace `src/main.js` with code that fills the list and fires confetti when the button is clicked:

```javascript
import confetti from "canvas-confetti";
import "./style.css";
import { players, rounds, standings } from "./scores.js";

const list = document.querySelector("#standings");

for (const { player, total } of standings(players, rounds)) {
  const item = document.createElement("li");
  item.textContent = `${player}: ${total} points`;
  list.append(item);
}

document.querySelector("#crown").addEventListener("click", () => {
  confetti();
});
```

Look at the first two lines. The browser on its own can't find a package by its bare name, `"canvas-confetti"`, and it can't import a CSS file into JavaScript. Vite can do both: it finds the package in `node_modules` and puts the CSS on the page. That's one reason projects use it.

![The Game Night Scoreboard in a browser at localhost:5173: Priya first with 22 points, then Maya and Jordan, with confetti bursting across the page after Crown the winner was clicked](https://www.ask-codey.com/lessons/TODO)

**Try it.** With `npm run dev` running, change `Crown the winner` to `Crown the champion` in `index.html` and save. Then give Jordan 12 points in Charades in `src/scores.js`, save, and watch the list change.

## What a bundler does

Your scoreboard is spread over `index.html`, `main.js`, `scores.js`, `style.css` and the confetti package's own modules. Sending each one separately makes a browser ask for one file after another, and every file carries spaces, comments and long names that only people need.

A **bundler** packs them, the way you pack a suitcase:

1. **It follows the imports**, starting from `index.html`: `main.js` imports `scores.js`, the confetti package and `style.css`, and so on, until it has every file the page needs.
2. **It combines them** into as few files as it can: here, one JavaScript file and one CSS file.
3. **It leaves out what nothing uses.** Code that's exported but never imported is dropped. This is called **tree shaking**: shake the tree, and the dead leaves fall off.
4. **It shrinks the code** (*minifies* it): it takes out spaces and comments and shortens names. The code does the same thing in fewer bytes.
5. **It fingerprints the file names**, adding a code made from the file's contents, such as `index-CGBecDMO.js`. Change the code, and the name changes, so a browser never shows a visitor yesterday's copy.

When you build, Vite is the bundler.

## npm run build and the dist folder

Stop the dev server with Ctrl+C and build:

```bash
npm run build
```

```text
> game-night-scoreboard@0.0.0 build
> vite build

vite v8.3.5 building client environment for production...
transforming...
✓ 7 modules transformed.
rendering chunks...
computing gzip size...
dist/index.html                  0.50 kB │ gzip: 0.31 kB
dist/assets/index-B7wmHYil.css   0.07 kB │ gzip: 0.09 kB
dist/assets/index-CGBecDMO.js   11.79 kB │ gzip: 4.74 kB

✓ built in 54ms
```

Your fingerprints and sizes will be a little different. Seven modules went in, and three files came out, in a new folder named **`dist`** (short for *distribution*): the page, one CSS file and one JavaScript file, about 12 kilobytes with the confetti package inside. *gzip* is the squeezed size a web server sends over the internet.

`dist` is the finished site, and it's the folder you put online; *Fast, Safe and Shipped* publishes one with GitHub Pages. Like `node_modules`, it's listed in Vite's `.gitignore`: you never edit it, and `npm run build` makes a fresh one any time. To check the built site before it goes anywhere, run `npm run preview`, which serves `dist` at http://localhost:4173.

**Try it.** Open `dist/assets` and look inside the JavaScript file. It's squeezed onto a couple of very long lines: your `standings` function is in there somewhere, minified. Then run `npm run preview`, open the address, and crown the winner from the built site.

## Check yourself

2 questions to try on the lesson page.

## So what

The dev server makes working on a page fast, and the build makes the finished page fast for everyone who visits it. Almost every modern web project, with or without a framework, is set up this way.

## Key terms

- Vite
- Development Server
- [Module Bundler](https://www.ask-codey.com/glossary/module-bundler)
- [Tree Shaking](https://www.ask-codey.com/glossary/tree-shaking)

## Cheat sheets

- [A Dev Server and Build with Vite](https://www.ask-codey.com/cheat-sheets/javascript-tooling-vite-cheat-sheet)

## Related

- [npm and Packages](https://www.ask-codey.com/lessons/javascript-tooling-npm-and-packages)
- [Clean Code with ESLint and Prettier](https://www.ask-codey.com/lessons/javascript-tooling-eslint-and-prettier)

Source: https://www.ask-codey.com/lessons/javascript-tooling-vite
