Open the dist/ directory after a build. Every file in there is something you'll serve. If you can name what each one is and why it exists, you understand what shipped.
The output shape
After npm run build:
dist/
index.html # the HTML shell with hashed asset links
assets/
index-[hash].js # the main JS bundle
index-[hash].css # extracted CSS
[Page]-[hash].js # one chunk per React.lazy import
[asset]-[hash].png # static assets, fingerprinted
The hashes are content-derived. If the file content doesn't change, the hash doesn't change, and browsers/CDNs cache it forever (immutable). When the content changes, the hash changes, and the cache invalidates automatically.
What gets bundled
- Your source — every TS/TSX/CSS/JSON file you imported (transitively).
- Your dependencies — only what you actually import. Tree-shaking removes unused exports.
- Static assets — images, fonts, SVGs imported via
import logo from './logo.png'. The import returns a hashed URL.
What doesn't get bundled: anything in public/ (copied as-is, no hashing), env vars without the VITE_ prefix (server-only or kept out of the client entirely).
Inspecting the bundle
Install rollup-plugin-visualizer (a Vite plugin via Rollup). It generates a treemap of your bundle so you can see which packages are biggest. Common findings: a stray import of a huge library you only needed for one helper, an icon set imported as a whole instead of cherry-picked.
npm run build and look at dist/. Open the JS file in a text editor (it's minified but readable enough). The bundle is your product surface. Knowing what's in it is what separates 'I built something' from 'I shipped something.'