Parcel 08 ๐ฆ HTML Handling โ Entry points & dependency discovery
An HTML file is the natural entry point for a Parcel application. Pointing Parcel at index.html tells it to parse the HTML, discover every reference to another fileโscripts, stylesheets, images, links, iframes, metadata URLsโand follow each one to build the complete asset graph. The HTML is not just a file that gets copied; it is the root of the dependency tree that determines what the entire build contains .
This chapter covers the HTML entry point, the dependency discovery rules for the major element types, the special handling of links and iframes that preserve human-readable filenames, and the multiple-entry-point configuration for multi-page applications.
Key point: Parcel rewrites every discovered URL to point at the correct output file. The HTML that ships is not the source HTML; it is a transformed document whose references resolve to the hashed bundles, processed images, and compiled stylesheets that Parcel produced.
Why HTML is the default entry point
The discovery problem. A JavaScript entry point requires the developer to import every asset explicitly. An HTML entry point inverts this: the HTML already contains the references, and Parcel discovers them by parsing the document. A <script src="./app.js"> becomes a dependency on app.js. A <link rel="stylesheet" href="./styles.css"> becomes a dependency on styles.css. The entire application is reachable from the HTML, so the HTML is the natural root .
The rewriting problem. The source HTML references ./app.js, but the build output contains app.a1b2c3.js. If the HTML were copied as-is, the browser would request a file that does not exist. Parcel’s HTML transformer parses the document, finds each reference, resolves it to the output asset, and rewrites the URL in the emitted HTML. The browser receives a document whose references point at the actual build artifacts .
a. The element types that Parcel discovers
Parcel’s HTML transformer traverses the DOM and collects dependencies from a defined set of elements and attributes.
Scripts. The <script> element references a JavaScript file or any file that compiles to JavaScript, such as TypeScript or JSX. The type="module" attribute marks the file as an ES module or CommonJS module and triggers differential bundling. If type is omitted, the file is treated as a classic script .
Stylesheets. The <link rel="stylesheet"> element references a CSS file or any file that compiles to CSS, such as Sass, Less, or Stylus .
Images. The <img> element’s src attribute is processed. The srcset attribute is also processed, allowing multiple image versions for different resolutions. The <picture> element and its <source> children are supported, including the image transformer’s query parameters for generating resized or reformatted variants from a single source .
Links and iframes. The <a> element’s href and the <iframe> element’s src are processed, with a special naming rule covered in the next section .
Media and embedded objects. The <video>, <audio>, <track>, <embed>, and <object> elements are processed, and their URLs are rewritten to include content hashes .
Metadata. Open Graph, Twitter Cards, Schema.org, and Microsoft pinned site metadata defined via <meta> tags are processed. Images and URLs referenced in these elements are rewritten with content hashes .
Web manifests. The <link rel="manifest"> element references a JSON or .webmanifest file. Parcel processes the URLs referenced in the icons and screenshots keys of the manifest .
JSON-LD. JSON-LD metadata embedded in <script> tags is processed by the @parcel/transformer-jsonld plugin, which is installed automatically when needed. Images referenced from the JSON-LD are rewritten .
b. The link and iframe naming exception
Files referenced by <a> or <iframe> do not receive content hashes in their output filenames. Every other asset type does. The reason is that these URLs are human-readable and are expected to be stable over time. A link to about.html should remain about.html, not about.a1b2c3.html, because users may bookmark it, share it, or type it into the address bar .
The stable naming means that the browser’s cache behavior for these files is different from the hashed bundles. A linked page must be revalidated or served with a shorter cache lifetime, because the filename does not change when the content does. Bundle naming can be overridden by Namer plugins if a project needs a different convention .
c. Multiple entry points
A multi-page application has more than one HTML entry point. Parcel accepts multiple entry files on the command line, or a glob pattern that expands to them.
# Multiple explicit entries
parcel index.html about.html contact.html
# Glob pattern
parcel ./**/*.html
The outputs are written to the same dist directory, preserving the relative path structure of the source files. A file at folder-1/index.html is served at /folder-1/index.html, not at /folder-1/ .
When multiple HTML entries share common dependencies, Parcel extracts the shared modules into a shared bundle if the module is large enough to justify the extra request. The default threshold is 30 KB; smaller shared modules are duplicated between pages to avoid the overhead of an additional HTTP request . The behavior is configured through the @parcel/bundler-default key in package.json.
Complete Example Session
<!-- ============================================
PART 1: THE SOURCE HTML
============================================ -->
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="./styles.css">
<link rel="icon" href="./favicon.ico">
</head>
<body>
<img src="./hero.jpg" alt="Hero">
<script type="module" src="./app.js"></script>
</body>
</html>
<!-- Parcel discovers:
styles.css, favicon.ico, hero.jpg, app.js -->
# ============================================
# PART 2: THE BUILD
# ============================================
parcel build index.html
# Output:
# dist/index.html
# dist/styles.a1b2c3.css
# dist/favicon.d4e5f6.ico
# dist/hero.g7h8i9.jpg
# dist/app.j1k2l3.js
<!-- ============================================
PART 3: THE REWRITTEN HTML
============================================ -->
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/styles.a1b2c3.css">
<link rel="icon" href="/favicon.d4e5f6.ico">
</head>
<body>
<img src="/hero.g7h8i9.jpg" alt="Hero">
<script type="module" src="/app.j1k2l3.js"></script>
</body>
</html>
<!-- Every reference points to the hashed output file. -->
<!-- ============================================
PART 4: THE LINK EXCEPTION
============================================ -->
<a href="./about.html">About</a>
<iframe src="./embed.html"></iframe>
<!-- Output:
<a href="/about.html">About</a>
<iframe src="/embed.html"></iframe>
No content hash. The filenames are stable. -->
<!-- ============================================
PART 5: IMAGE SRCSET
============================================ -->
<img
src="./logo.png"
srcset="./logo@2x.png 2x"
alt="Logo"
>
<!-- Both logo.png and logo@2x.png are processed.
Each gets its own hashed output. -->
<!-- ============================================
PART 6: THE PICTURE ELEMENT
============================================ -->
<picture>
<source type="image/webp" srcset="./image.jpg?as=webp&width=400">
<img src="./image.jpg" alt="Image">
</picture>
<!-- The query parameters generate a WebP variant
from the JPEG source. -->
<!-- ============================================
PART 7: META OPEN GRAPH
============================================ -->
<meta property="og:image" content="./og-image.png">
<!-- Output: <meta property="og:image" content="/og-image.a1b2c3.png">
The URL is rewritten with a content hash. -->
<!-- ============================================
PART 8: THE WEB MANIFEST
============================================ -->
<link rel="manifest" href="./manifest.webmanifest">
<!-- Parcel processes the manifest and the URLs
referenced in its icons and screenshots keys. -->
<!-- ============================================
PART 9: JSON-LD
============================================ -->
<script type="application/ld+json">
{
"logo": "./logo.png"
}
</script>
<!-- The logo URL is processed and rewritten. -->
# ============================================
# PART 10: MULTIPLE ENTRIES
# ============================================
parcel index.html about.html
# Both pages are built.
# Shared dependencies above the threshold
# are extracted into a shared bundle.
The ten parts covered the source HTML, the build, the rewritten HTML, the link exception, srcset, the picture element, Open Graph metadata, the web manifest, JSON-LD, and multiple entries.
Quick Reference
Discovered elements
| Element | Attribute | Processed |
|---|---|---|
<script> | src | Yes |
<link rel="stylesheet"> | href | Yes |
<link rel="manifest"> | href | Yes |
<img> | src, srcset | Yes |
<picture> / <source> | srcset | Yes |
<a> | href | Yes (no hash) |
<iframe> | src | Yes (no hash) |
<video>, <audio>, <track> | src | Yes |
<meta property="og:image"> | content | Yes |
<script type="application/ld+json"> | inline | Yes |
Script attributes
| Attribute | Effect |
|---|---|
type="module" | ES module, triggers differential bundling |
async | Isolated, cannot share dependencies |
defer | Automatic for type="module" |
Multiple entries
| Method | Command |
|---|---|
| Explicit | parcel index.html about.html |
| Glob | parcel ./**/*.html |
| Shared bundle threshold | 30 KB default |
URL rewriting
| Reference | Hash added |
|---|---|
| Script, CSS, image, media | Yes |
<a>, <iframe> | No |
Best Practices
โ Do This:
<!-- Use type="module" for the main script -->
<script type="module" src="./app.js"></script> // โ
<!-- Reference assets from HTML so Parcel discovers them -->
<img src="./hero.jpg" alt="Hero"> // โ
# Use a glob for multi-page apps
parcel ./**/*.html // โ
โ Don’t Do This:
<!-- Use a classic script without type="module" -->
<script src="./app.js"></script> // โ (no differential bundling)
<!-- Hardcode hashed filenames in the source -->
<script src="./app.a1b2c3.js"></script> // โ
# Build only one page of a multi-page app
parcel index.html # ignores about.html // โ
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Asset not processed | Referenced from a JavaScript string, not HTML | Import it from JS or reference it from HTML |
| Linked page has a hash | The link uses <a> but Parcel added a hash | Check the output; <a> should not be hashed |
| Multi-page app has duplicate code | Shared module below 30 KB threshold | Configure @parcel/bundler-default |
| Manifest icons not processed | The manifest is not referenced from HTML | Add <link rel="manifest"> |
async script duplicates dependencies | Async scripts are isolated | Use defer or type="module" |
Real-World Examples
1. Basic HTML entry
<script type="module" src="./app.js"></script>
2. Stylesheet
<link rel="stylesheet" href="./styles.css">
3. Image with srcset
<img src="./logo.png" srcset="./logo@2x.png 2x">
4. Picture element
<picture>
<source type="image/webp" srcset="./image.jpg?as=webp">
<img src="./image.jpg">
</picture>
5. Open Graph image
<meta property="og:image" content="./og.png">
6. Linked page
<a href="./about.html">About</a>
7. Iframe
<iframe src="./embed.html"></iframe>
8. Manifest
<link rel="manifest" href="./manifest.webmanifest">
9. JSON-LD
<script type="application/ld+json">{"logo":"./logo.png"}</script>
10. Multiple entries
parcel index.html about.html contact.html
Visual
The dependency discovery flow
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ index.html โ
โ โ โ
โ โโโ <link rel="stylesheet" href="styles.css"> โ
โ โ โโโ styles.css โ
โ โ โ
โ โโโ <script type="module" src="app.js"> โ
โ โ โโโ app.js โโโบ imports ./utils.js โ
โ โ โ
โ โโโ <img src="hero.jpg"> โ
โ โ โโโ hero.jpg โ
โ โ โ
โ โโโ <a href="about.html"> โ
โ โโโ about.html โ
โ โ
โ Parcel builds the graph from these references. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
URL rewriting
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SOURCE OUTPUT โ
โ โ
โ ./styles.css /styles.a1b2c3.css โ
โ ./app.js /app.j1k2l3.js โ
โ ./hero.jpg /hero.g7h8i9.jpg โ
โ ./about.html /about.html (no hash) โ
โ โ
โ Every reference is rewritten to the output path. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Multiple entries
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ index.html โโโ โ
โ โโโ shared dependency โโโบ shared.a1b2c3.js โ
โ about.html โโโ โ
โ โ
โ If the shared module is above the threshold, โ
โ it is extracted into a separate bundle. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The link exception
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ <a href="about.html"> โ
โ <iframe src="embed.html"> โ
โ โ
โ These stay stable. No content hash. โ
โ โ
โ Reason: human-readable URLs must not change. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| Default entry | HTML file |
| Discovery method | DOM traversal |
| Script module flag | type="module" |
| Stylesheet | <link rel="stylesheet"> |
| Image attributes | src, srcset |
| Link/iframe hashing | No (stable filenames) |
| Multiple entries | CLI arguments or glob |
| Shared bundle threshold | 30 KB default |
Key takeaways:
- HTML is the natural entry point because it already contains the references. Parcel parses the document, discovers every dependency, and builds the asset graph from there. The developer does not need to import every asset in JavaScript .
- Parcel rewrites every discovered URL to the output path. The source HTML references
./app.js; the output references/app.a1b2c3.js. The browser receives a document whose references resolve to the actual build artifacts . - Links and iframes are the exception to content hashing. Files referenced by
<a>or<iframe>keep their original filenames because those URLs are human-readable and must remain stable. Every other asset type gets a content hash . type="module"on the script tag triggers differential bundling. When thebrowserslistincludes browsers without native ES module support, Parcel generates thenomodulefallback automatically .- Multiple HTML entries build a multi-page application. Parcel accepts multiple files or a glob. Shared dependencies above the 30 KB threshold are extracted into a shared bundle; smaller ones are duplicated .
- Metadata is processed too. Open Graph images, web manifests, and JSON-LD are parsed, and their URLs are rewritten with content hashes .
Remember: HTML is the entry point that makes Parcel’s zero-configuration model work. The HTML contains the references, Parcel follows them, and the output HTML contains the rewritten URLs. The <script type="module"> tag is the signal for ES module handling and differential bundling. The <a> and <iframe> elements are the exception to content hashing because their URLs must stay human-readable. For a multi-page app, list the HTML files or use a glob, and let Parcel handle the shared bundle extraction.
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!