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

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:

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:

└  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:

{
  "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:

npm run dev
> 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:

npm install canvas-confetti

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

<!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:

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

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:

npm run build
> 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

Cheat sheets

Related