| | |

Parcel 01 ๐Ÿ“ฆ Why Parcel โ€” Zero-config bundling & the asset graph model

โฑ๏ธ Estimated read: 11 min | |

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

ConceptMeaning
HTML entryThe build starts from the HTML file, not the JS
Asset graphThe complete map of every file and dependency
Transform phaseEach asset is compiled and its dependencies extracted
Bundle phaseThe asset graph is grouped into output bundles
Zero-configThe default inference covers the common cases
.parcelrcThe optional JSON configuration

The Plugin Stages

StagePurpose
ResolverTurn the import specifiers into file paths
TransformerCompile the assets and extract dependencies
BundlerGroup the assets into bundles
NamerAssign the output filenames
RuntimeInsert the code into the bundles
PackagerMerge the assets of a common type
OptimizerMinify and compress the bundles
ValidatorRun the linting and type checking

The Zero-Config Defaults

FeatureDefault behavior
TypeScriptTranspiled with SWC
CSSParsed and its dependencies extracted
ImagesOptimized and hashed
Code splittingAutomatic shared bundles
CachingPersistent, 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

PitfallWhy It HappensFix
The CSS is not processedThe entry is the JS, not the HTMLUse the HTML entry
The modern syntax is not transpiledNo browserslistAdd the browserslist to the package.json
The custom file type failsNo transformerAdd the transformer to the .parcelrc
The build is slowThe cache is coldThe 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

ItemValue
Parcel philosophyZero-config with the optional extensibility
Entry pointThe HTML file
Asset graphThe complete map of the files and the dependencies
Transform phaseThe asset compilation and the dependency extraction
.parcelrcThe JSON configuration file
Plugin stagesResolver, transformer, bundler, namer, runtime, packager, optimizer, validator
Default TypeScriptSWC
Default transpilationNone 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 .parcelrc exists 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 .parcelrc extends or overrides .
  • Parcel 2 does no transpilation by default. The modern syntax is preserved unless the browserslist is set. The browserslist is 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.


Was this helpful?


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!