Parcel Bundler
Parcel is a zero-configuration web bundler that requires no setup. It automatically detects and configures transforms, bundling, dev server, and production optimizations based on your project files. Parcel v2 is written in Rust for native performance.
| 1 | # Install Parcel |
| 2 | npm install --save-dev parcel |
| 3 | |
| 4 | # Add scripts to package.json |
| 5 | # "scripts": { |
| 6 | # "dev": "parcel src/index.html", |
| 7 | # "build": "parcel build src/index.html", |
| 8 | # "preview": "parcel dist/index.html" |
| 9 | # } |
| 10 | |
| 11 | # Start dev server (zero config!) |
| 12 | npm run dev |
| 13 | |
| 14 | # Production build |
| 15 | npm run build |
| 16 | |
| 17 | # Parcel auto-detects: |
| 18 | # - TypeScript (transpiles) |
| 19 | # - JSX/TSX (transpiles) |
| 20 | # - CSS/SCSS/Less (processes) |
| 21 | # - Images (optimizes, generates hashes) |
| 22 | # - JSON imports (parses) |
| 23 | # - HTML (processes references) |
Targets tell Parcel where to output bundles and what browsers/environments to support. Parcel v2 uses browserslist for browser targeting.
| 1 | // package.json — configure targets |
| 2 | { |
| 3 | "targets": { |
| 4 | "main": { |
| 5 | "context": "browser", |
| 6 | "outputFormat": "global", |
| 7 | "distDir": "dist" |
| 8 | }, |
| 9 | "module": { |
| 10 | "context": "browser", |
| 11 | "outputFormat": "esmodule", |
| 12 | "distDir": "dist/esm", |
| 13 | "isLibrary": true, |
| 14 | "sourceMap": true |
| 15 | }, |
| 16 | "node": { |
| 17 | "context": "node", |
| 18 | "outputFormat": "commonjs", |
| 19 | "distDir": "dist/node", |
| 20 | "isLibrary": true, |
| 21 | "engines": { "node": ">=18" } |
| 22 | } |
| 23 | }, |
| 24 | "browserslist": ">= 0.5%, last 2 versions, not dead" |
| 25 | } |
| 1 | // Parcel automatically code-splits on dynamic imports |
| 2 | // No configuration needed! |
| 3 | |
| 4 | // Route-based splitting |
| 5 | const Dashboard = React.lazy(() => import("./pages/Dashboard")); |
| 6 | const Settings = React.lazy(() => import("./pages/Settings")); |
| 7 | |
| 8 | // Conditional imports |
| 9 | async function loadPlugin(name: string) { |
| 10 | const plugin = await import(`./plugins/${name}`); |
| 11 | return plugin.default; |
| 12 | } |
| 13 | |
| 14 | // CSS splitting — each component's CSS is a separate chunk |
| 15 | import "./Button.css"; // Automatically split per import |
| 16 | |
| 17 | // Shared chunks — Parcel deduplicates shared modules |
| 18 | // between routes automatically |
| 19 | |
| 20 | // Named exports with dynamic import |
| 21 | const { formatDate, parseDate } = await import("./date-utils"); |
| 22 | // Only the used exports are bundled (tree-shaking) |
| 1 | // package.json — workspace configuration |
| 2 | { |
| 3 | "workspaces": ["packages/*"], |
| 4 | "targets": { |
| 5 | "default": { |
| 6 | "distDir": "dist" |
| 7 | } |
| 8 | } |
| 9 | } |
| 10 | |
| 11 | // Parcel resolves imports across workspaces |
| 12 | // If package-a imports from package-b, Parcel |
| 13 | // uses the source directly (no build step needed) |
| 14 | // This enables instant HMR across packages |
info
Parcel starts a development server from an HTML entry with Hot Module Replacement enabled by default. CSS updates typically apply without a full reload; JS HMR depends on the module accepting updates (framework integrations improve this).
| 1 | # Serve with HMR |
| 2 | npx parcel src/index.html --port 1234 --open |
| 3 | |
| 4 | # Disable HMR when debugging full reloads |
| 5 | npx parcel src/index.html --no-hmr |
| 6 | |
| 7 | # HTTPS locally (useful for secure-context APIs) |
| 8 | npx parcel src/index.html --https |
info
Parcel v2 uses a transformer pipeline (often Rust-backed) to compile TypeScript, JSX, CSS modules, Sass, images, and more based on file extensions and package.json metadata.
| Asset type | Default behavior | Configure via |
|---|---|---|
| .ts / .tsx | Transpile (not full typecheck) | tsconfig + engines/browserslist |
| .css / .scss | Bundle, minify in prod | PostCSS config if present |
| CSS modules | *.module.css scoped | Naming conventions |
| Images | Hash + optimize | query params / image pipelines |
| .json | Importable modules | — |
warning
When zero-config is not enough, extend the pipeline with .parcelrc. Extend the default config instead of replacing it wholesale.
| 1 | { |
| 2 | "extends": ["@parcel/config-default"], |
| 3 | "transformers": { |
| 4 | "*.{gl,glsl}": ["...", "@parcel/transformer-glsl"] |
| 5 | }, |
| 6 | "optimizers": { |
| 7 | "*.js": ["...", "@parcel/optimizer-swc"] |
| 8 | } |
| 9 | } |
note
Parcel inlines process.env.NODE_ENV and supports .env files. Only expose values intended for the browser — treat client bundles as public.
| 1 | # .env — local defaults |
| 2 | # .env.production — production build overrides |
| 3 | |
| 4 | # Access in code (bundler replaces at build time) |
| 5 | # process.env.API_URL |
| 6 | |
| 7 | # Production build |
| 8 | NODE_ENV=production npx parcel build src/index.html |
danger
| Concern | Parcel behavior | Practice |
|---|---|---|
| Minification | Enabled in parcel build | Keep source maps for error tracking |
| Content hashing | Hashed filenames by default | Cache-Control long-lived on hashed assets |
| Scope hoisting | ESM concatenation for size | Prefer ESM dependencies |
| Differential bundling | Modern + legacy when needed | Set realistic browserslist |
| 1 | npx parcel build src/index.html \ |
| 2 | --dist-dir dist \ |
| 3 | --no-source-maps # only if you upload maps another way — usually keep maps |
| 4 | |
| 5 | # Inspect output |
| 6 | ls -lh dist |
| Aspect | Parcel | Vite |
|---|---|---|
| Config philosophy | Zero-config HTML entry | Config + rich plugins |
| Dev architecture | Bundled dev graph | Native ESM + esbuild deps |
| Ecosystem | Smaller plugin set | Large framework templates |
| Best when | HTML-first apps, low config | SPA frameworks, plugin needs |
best practice
- Marketing sites and multi-page HTML apps with light JS.
- Prototypes where writing webpack/vite config is pure overhead.
- Monorepos that benefit from source resolution across packages.
- Avoid as the sole tool for Module Federation-heavy micro-frontends — prefer Webpack/Rspack.
Community
Get help on Slack, Discord or VIP
Stuck on a guide? Join the community and ask.