Parcel 01 ๐ฆ Why Parcel โ Zero-config bundling & the asset graph model
Parcel is a web application bundler built around a single promise: it works out of the box. Where webpack requires a configuration file to understand what to do with a .ts file, a CSS import, or an image reference, Parcel infers the transformation pipeline from the file itself and the project’s existing configuration files. The previous chapters covered esbuild’s native-code speed and Vite’s unbundled dev server; this chapter covers Parcel’s different bet: that the developer should not have to describe the build, only the application.
The “zero-config” label has caused confusion. It never meant that Parcel cannot be configured. It meant that Parcel attempts to infer as much as possible from the code itself and from the existing configuration files of other tools, such as .babelrc or browserslist . Parcel 2 made this explicit: the default configuration covers the majority of applications, and the .parcelrc file exists for the edge cases that need a custom resolver, transformer, or bundler . The architecture that makes the inference possible is the asset graph.
Key point: Parcel does not start from a JavaScript entry point. It starts from the HTML file and follows every dependency a browser would followโJavaScript, CSS, images, fonts, workers, and URLs . The result is an asset graph: a complete map of every file the application needs and how the files depend on one another. The bundling step then groups the assets into output bundles based on the graph, not based on a manually declared entry list.
Why Parcel exists
The configuration problem.
A webpack configuration is a description of the build pipeline written in JavaScript. It declares the loaders, the plugins, the output settings, and the entry points. The description must be maintained alongside the application, and it often grows into a second codebase that only one person on the team understands. Parcel’s bet is that most of this description is unnecessary. The extension of a file (.ts, .scss, .png) and the content of the import statement contain enough information to select the transformer and resolve the dependency . The zero-config default is not a limitation; it is a recognition that the common case does not need a pipeline description.
The asset-graph problem.
A JavaScript bundler that only understands JavaScript is blind to the rest of the application. The CSS imported in a component, the image referenced in the stylesheet, the worker instantiated from a URLโthese are dependencies too, and they have their own transformation and output requirements. Parcel’s asset graph treats every file as an asset with a type and a set of dependencies, regardless of whether the file contains JavaScript . The graph is built during the transform phase: each transformer extracts the dependencies from the asset it processes, and those dependencies become new nodes in the graph . The result is a build that understands the whole application, not just the code.
The optional-configurability problem.
The zero-config default is a starting point, not a cage. Parcel 2’s plugin system covers every stage of the build: the resolvers turn import specifiers into file paths, the transformers compile assets and extract dependencies, the bundlers group the assets into bundles, the namers assign the output filenames, the packagers merge the assets of a common type, and the optimizers minify the result . Each plugin type has a specific API, and the .parcelrc file configures the plugins in a simple JSON format . The architecture is designed so that the default config can be extended without replacing it.
a. The HTML entry and the asset graph
Parcel projects start with an HTML file. The build follows the <script> tags, the <link> tags, and the asset references, and every discovered file becomes a node in the asset graph.
<!-- index.html -->
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<script type="module" src="./index.ts"></script>
<img src="./logo.png" alt="Logo" />
</body>
</html>
The build reads the index.html and discovers three dependencies: the styles.css, the index.ts, and the logo.png. Each becomes an asset in the graph. The styles.css is transformed by the CSS transformer, and any @import or url() inside it becomes further dependencies. The index.ts is transformed by the TypeScript transformer, and its imports become further dependencies . The logo.png is processed by the image transformer and becomes an output asset with a content hash.
The graph grows until no new files are discovered. The bundling step then takes this complete graph and produces the output bundles.
b. The transform phase and the dependency extraction
The transform phase is where each asset is processed and its dependencies are extracted. This is the phase that builds the graph.
// A custom transformer extracts the dependencies from the asset
import { Transformer } from '@parcel/plugin'
export default new Transformer({
async transform({ asset }) {
const code = await asset.getCode()
// Extract the import statements and add them as dependencies
const dependencies = extractImports(code)
dependencies.forEach(dep => {
asset.addDependency({
specifier: dep.specifier,
specifierType: 'esm',
})
})
return [asset]
},
})
The transformer receives the asset, reads its code, extracts the import specifiers, and adds them as dependencies . The resolver then turns each specifier into a file path, and the new asset is transformed in turn. The cycle repeats until the graph is complete.
This is the mechanism that makes the zero-config inference work: the transformer for the file type knows how to extract the dependencies for that type. The TypeScript transformer knows about the import statements; the CSS transformer knows about the @import rules and the url() references; the HTML transformer knows about the <script>, <link>, and <img> tags. The graph is built from the file content, not from a configuration declaration.
c. The optional configuration and the plugin stages
The .parcelrc file is the configuration entry point, and it is JSON, not JavaScript . It names the plugins for each stage of the build.
{
"extends": "@parcel/config-default",
"transformers": {
"*.dart": ["parcel-transformer-dart"]
}
}
The extends field inherits the default configuration, and the transformers field adds a transformer for the .dart extension . The configuration is optional; the default config works without this file. The .parcelrc is the escape hatch for the cases the default inference cannot handle, such as a custom file type or a custom resolution algorithm.
The plugin stages are the architecture that makes the optional configurability possible. The resolvers handle the resolution, the transformers handle the compilation, the bundlers handle the grouping, the namers handle the filenames, the runtimes handle the code insertion, the packagers handle the merging, the optimizers handle the minification, and the validators handle the background checks . Each stage has a strict contract, and the default config implements each stage with the standard plugins.
Complete Example Session
# ============================================
# PART 1: THE PROJECT STRUCTURE
# ============================================
# index.html
# src/index.ts
# src/styles.css
# src/logo.png
# ============================================
# PART 2: THE HTML ENTRY
# ============================================
# The index.html references the script, the stylesheet, and the image
cat index.html
# <script type="module" src="./src/index.ts"></script>
# <link rel="stylesheet" href="./src/styles.css" />
# ============================================
# PART 3: THE BUILD COMMAND
# ============================================
npx parcel build index.html
# The build follows the dependencies and produces the output
# ============================================
# PART 4: THE ASSET GRAPH DISCOVERY
# ============================================
# The build discovers:
# - index.html
# - src/index.ts (and its imports)
# - src/styles.css (and its url() references)
# - src/logo.png
# ============================================
# PART 5: THE OUTPUT
# ============================================
ls dist/
# index.html, the hashed JS, the hashed CSS, the hashed image
# ============================================
# PART 6: THE OPTIONAL CONFIG
# ============================================
# A custom transformer for a non-standard file type
cat .parcelrc
# { "extends": "@parcel/config-default", "transformers": { "*.dart": [...] } }
# ============================================
# PART 7: THE DEV SERVER
# ============================================
npx parcel index.html
# The dev server starts with HMR
The seven parts demonstrate the Parcel model: the HTML entry, the dependency discovery, the build, the output, the optional config, and the dev server.
Quick Reference
The Parcel Concepts
| Concept | Meaning |
|---|---|
| HTML entry | The build starts from the HTML file, not the JS |
| Asset graph | The complete map of every file and dependency |
| Transform phase | Each asset is compiled and its dependencies extracted |
| Bundle phase | The asset graph is grouped into output bundles |
| Zero-config | The default inference covers the common cases |
.parcelrc | The optional JSON configuration |
The Plugin Stages
| Stage | Purpose |
|---|---|
| Resolver | Turn the import specifiers into file paths |
| Transformer | Compile the assets and extract dependencies |
| Bundler | Group the assets into bundles |
| Namer | Assign the output filenames |
| Runtime | Insert the code into the bundles |
| Packager | Merge the assets of a common type |
| Optimizer | Minify and compress the bundles |
| Validator | Run the linting and type checking |
The Zero-Config Defaults
| Feature | Default behavior |
|---|---|
| TypeScript | Transpiled with SWC |
| CSS | Parsed and its dependencies extracted |
| Images | Optimized and hashed |
| Code splitting | Automatic shared bundles |
| Caching | Persistent, content-hashed |
Best Practices
โ Do This:
# Start from the HTML entry
npx parcel build index.html # โ
# Let the default config handle the common file types
# Add the .parcelrc only for the edge cases # โ
# Use the HTML file as the entry for the dev server
npx parcel index.html # โ
# Use the browserslist for the target control
# In the package.json # โ
โ Don’t Do This:
# Use the JavaScript entry as the Parcel entry
npx parcel build src/index.ts # The HTML dependencies missed # โ
# Write a webpack-style config
# Parcel's config is the JSON .parcelrc # โ
# Assume the zero-config means non-configurable
# The .parcelrc exists for the edge cases # โ
# Forget the browserslist
# Parcel 2 does no transpilation by default without it # โ
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| The CSS is not processed | The entry is the JS, not the HTML | Use the HTML entry |
| The modern syntax is not transpiled | No browserslist | Add the browserslist to the package.json |
| The custom file type fails | No transformer | Add the transformer to the .parcelrc |
| The build is slow | The cache is cold | The subsequent builds use the cache |
Real-World Examples
1. The HTML entry
npx parcel build index.html
2. The dev server
npx parcel index.html
3. The .parcelrc
{ "extends": "@parcel/config-default" }
4. The custom transformer
{ "transformers": { "*.dart": ["parcel-transformer-dart"] } }
5. The browserslist
{ "browserslist": ["last 2 versions"] }
6. The code splitting
import('./lazy').then(module => module.default())
7. The shared bundle
<script type="module" src="./home.ts"></script>
<script type="module" src="./profile.ts"></script>
8. The SVG import
<img src="./logo.svg" />
9. The URL reference
background: url('./bg.png');
10. The build output
ls dist/
# index.html, index.[hash].js, index.[hash].css, logo.[hash].png
Visual
The Asset Graph
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ THE BUILD STARTS FROM THE HTML AND FOLLOWS THE DEPENDENCIES โ
โ โ
โ index.html โ
โ โโโ <link href="./styles.css"> โ
โ โ โโโ url('./bg.png') โ
โ โโโ <script src="./index.ts"> โ
โ โ โโโ import './utils' โ
โ โ โโโ import 'react' โ
โ โโโ <img src="./logo.png"> โ
โ โ
โ Every discovered file becomes an asset in the graph. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Plugin Stages
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ EVERY STAGE IS A PLUGIN โ
โ โ
โ Resolver โ Transformer โ Bundler โ Namer โ Runtime โ
โ โ Packager โ Optimizer โ Validator โ
โ โ
โ The default config implements every stage with the โ
โ standard plugins. The .parcelrc extends or overrides. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Zero-Config vs the Optional Config
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ZERO-CONFIG IS THE DEFAULT, NOT THE ONLY OPTION โ
โ โ
โ The default config: โ
โ โโโ TypeScript with SWC โ
โ โโโ CSS, images, fonts โ
โ โโโ Code splitting โ
โ โโโ Caching โ
โ โ
โ The .parcelrc: โ
โ โโโ The custom resolvers, transformers, and bundlers โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| Parcel philosophy | Zero-config with the optional extensibility |
| Entry point | The HTML file |
| Asset graph | The complete map of the files and the dependencies |
| Transform phase | The asset compilation and the dependency extraction |
.parcelrc | The JSON configuration file |
| Plugin stages | Resolver, transformer, bundler, namer, runtime, packager, optimizer, validator |
| Default TypeScript | SWC |
| Default transpilation | None without the browserslist |
Key takeaways:
- Parcel starts from the HTML, not the JavaScript. The build follows every dependency a browser would follow, including the CSS, the images, the fonts, and the workers. The asset graph is the complete map of these dependencies .
- Zero-config means inference, not non-configurability. Parcel infers the transformation pipeline from the file extensions and the existing configuration files. The
.parcelrcexists for the edge cases that the inference cannot handle . - The asset graph is built during the transform phase. Each transformer compiles the asset and extracts its dependencies. The resolved dependencies become new nodes in the graph, and the cycle repeats until the graph is complete .
- The plugin system covers every stage. The resolvers, the transformers, the bundlers, the namers, the runtimes, the packagers, the optimizers, and the validators are all plugins. The default config implements every stage, and the
.parcelrcextends or overrides . - Parcel 2 does no transpilation by default. The modern syntax is preserved unless the
browserslistis set. Thebrowserslistis the mechanism that enables the transpilation for the older browsers . - The code splitting is automatic. The dynamic
import()creates the separate bundles, and the shared dependencies are deduplicated into the shared bundles. The configuration thresholds are tuned for HTTP/2 .
Remember: Parcel’s zero-config bundling is built on the asset graph. The HTML entry starts the traversal, the transformers extract the dependencies, and the graph grows until the application is fully mapped. The default config handles the common cases, and the .parcelrc handles the edge cases. The result is a build that understands the whole applicationโnot just the JavaScriptโwithout requiring the developer to describe the pipeline.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!