Parcel 22 ๐ฆ Named Pipelines โ Custom transform chains for specific files
A named pipeline is a URL-scheme prefix in an import specifier that selects a different transformer chain for the same file type. The default Parcel config already uses them: importing an image normally returns a URL to an external file, but import logo from 'data-url:./logo.png' inlines the image as a data URL instead . The mechanism is a convention in .parcelrc: a transformer glob can begin with a prefix like data-url: or jsx:, and that prefix becomes available as an import scheme. The developer chooses per-import which chain runs, and the same file can be processed in multiple ways within a single build.
The named pipeline is the answer to a limitation that glob-based pipelines cannot solve. A glob matches the file path, and the matched chain runs for every import of that file. If an SVG should be a URL in one component and a React component in another, a glob cannot express the distinction. The named pipeline can. The import specifier carries the choice, and the .parcelrc defines the chain. The file is processed differently depending on how it is imported, and the two results coexist in the same build .
Key point: A named pipeline is defined in .parcelrc by adding a URL-scheme prefix to the glob, like "jsx:*.svg". The import statement uses the prefix, like import Icon from 'jsx:./icon.svg'. The "..." syntax runs the default transformer chain first, then the custom plugin. Without "...", the custom plugin replaces the default chain entirely.
Why named pipelines exist
The per-import choice problem. Parcel’s glob-based pipelines are file-type-based. A *.svg glob applies to every SVG in the project. There is no way to say “this SVG is a React component, that SVG is a URL, and the other SVG is a raw string” with globs alone. Named pipelines move the choice from the build configuration to the import statement. The developer writes jsx:./star.svg for the component, ./star.svg for the URL, and bundle-text:./star.svg for the string. The same file, three different results, three different imports .
The "..." extension mechanism. A named pipeline is a transformer chain, and the "..." syntax is what makes it a chain rather than a replacement. The default SVG transformer parses the SVG and produces an asset. The custom @parcel/transformer-svg-jsx transformer converts the parsed asset into a React component. The "..." runs the default first, then the custom plugin. Without it, the custom plugin receives the raw SVG file and may fail because it expects a parsed asset . The order is the mechanism.
The optimizer inheritance. Named pipelines are supported for optimizers as well as transformers, but the inheritance rule differs. For transformers, the pipeline is specified in the import statement that references the asset. For optimizers, the pipeline is inherited from the entry asset of the bundle . This means a named pipeline on a JavaScript import affects the transformers for that file, but the optimizers for the resulting bundle are determined by the bundle’s entry point. The distinction matters for bundle-level transformations like minification, which run after the bundle is formed.
The reserved prefix constraint. Not every string can be a named pipeline prefix. Parcel reserves the URL schemes node:, npm:, http:, https:, data:, tel:, and mailto: for its own resolution logic. A pipeline key like "http:*" throws a “reserved pipeline” error . The prefix must be a custom scheme that does not collide with the built-in ones. The common prefixesโjsx:, url:, bundle-text:, data-url:โare not reserved because they are not Parcel URL schemes.
a. Defining a named pipeline in .parcelrc
The definition is a transformer glob with a prefix. The prefix is the scheme, and the rest of the glob is the file pattern. The "..." inserts the default chain at the position it appears.
// .parcelrc โ a jsx: pipeline for SVG
{
"extends": "@parcel/config-default",
"transformers": {
// The jsx:*.svg glob matches 'jsx:./icon.svg'.
// The "..." runs the default SVG transformer first.
"jsx:*.svg": ["...", "@parcel/transformer-svg-jsx"],
// The catch-all for the jsx: prefix.
// Without it, a jsx: import of a non-SVG file fails.
"jsx:*": ["..."]
}
}
The jsx:*.svg entry is the specific pipeline. The jsx:* entry is the fallback for any jsx: import that does not match the more specific glob. The order matters: the specific glob is listed first, so it wins for SVG files, and the catch-all handles the rest . The "..." in the specific pipeline runs the default SVG transformer before the JSX transformer. The "..." in the catch-all runs the default chain for the file type.
// .parcelrc โ an arraybuffer: pipeline
{
"extends": "@parcel/config-default",
"transformers": {
// The arraybuffer: prefix matches any file.
// The custom transformer converts the compiled asset to an ArrayBuffer.
"arraybuffer:*": ["...", "parcel-transformer-arraybuffer"]
}
}
The arraybuffer: pipeline is the example from the Parcel documentation. The "..." processes the file as it normally would, and the custom transformer converts the result to an ArrayBuffer. The import statement requests the pipeline with the prefix: import buffer from 'arraybuffer:./logo.png' .
b. Using the pipeline in an import statement
The import statement is where the named pipeline is selected. The prefix is the scheme, and the path follows. The bundler matches the import against the .parcelrc entries, and the matching chain runs.
// A jsx: import of an SVG
import Star from 'jsx:./star.svg';
// A default import of the same SVG
import starUrl from './star.svg';
The two imports reference the same file. The first runs the jsx:*.svg pipeline and produces a React component. The second runs the default *.svg pipeline and produces a URL string. The two results coexist because the pipeline is part of the import, not part of the file .
// A bundle-text: import of a CSS file
import cssText from 'bundle-text:./styles.css';
// The cssText variable contains the compiled CSS as a string.
// The default import would return a URL to the CSS file.
The bundle-text: pipeline is defined in the default Parcel config. It runs the default CSS transformer and then the @parcel/transformer-inline-string transformer, which marks the asset as inline and returns the content as a string . The import statement selects the pipeline, and the default config provides the chain. A project that needs a different inline encodingโa data URL, an ArrayBuffer, a base64 stringโdefines a custom named pipeline with its own transformer .
Complete Example Session
// ============================================
// PART 1: THE .parcelrc WITH A jsx: PIPELINE
// ============================================
// .parcelrc
{
"extends": "@parcel/config-default",
"transformers": {
// The specific pipeline for SVG files.
// The "..." runs the default SVG transformer first.
"jsx:*.svg": ["...", "@parcel/transformer-svg-jsx"],
// The catch-all for the jsx: prefix.
"jsx:*": ["..."]
}
}
// ============================================
// PART 2: THE IMPORT STATEMENT
// ============================================
// App.jsx
import Star from 'jsx:./star.svg';
import starUrl from './star.svg';
export function App() {
return (
<div>
<Star /> {/* The React component */}
<img src={starUrl} /> {/* The URL string */}
</div>
);
}
// ============================================
// PART 3: THE TYPESCRIPT DECLARATION
// ============================================
// types/svg.d.ts
declare module "jsx:*.svg" {
import { ComponentType, SVGProps } from "react";
const SVGComponent: ComponentType<SVGProps<SVGSVGElement>>;
export default SVGComponent;
}
// Without this, TypeScript reports an error on the jsx: import.
// ============================================
// PART 4: THE OPTIMIZER INHERITANCE
// ============================================
// Named pipelines for optimizers inherit from the entry asset.
// A pipeline on a JavaScript import affects the transformers
// for that file, but the optimizers for the resulting bundle
// are determined by the bundle's entry point.
// ============================================
// PART 5: THE RESERVED PREFIX
// ============================================
// โ A pipeline with a reserved prefix:
// "http:*": ["my-http-transformer"]
//
// The "http:" scheme is reserved for Parcel's resolution.
// The build throws a "reserved pipeline" error .
// โ
A custom prefix:
// "myproto:*": ["my-transformer"]
// ============================================
// PART 6: THE "..." POSITION
// ============================================
// The "..." can be placed at the beginning, end, or middle.
// The position determines the order of the default chain
// relative to the custom plugins.
// ["...", "custom"] โ default first, then custom
// ["custom", "..."] โ custom first, then default
// ["custom1", "...", "custom2"] โ custom1, default, custom2
// ============================================
// PART 7: THE ONLY-ONE-SPREAD RULE
// ============================================
// A pipeline can contain only one "...".
// Two spreads are ambiguous: which one inserts the default?
// The build throws "Only one spread element can be included
// in a config pipeline" .
// โ ["...", "custom", "..."]
// โ
["...", "custom"]
// ============================================
// PART 8: THE CATCH-ALL NECESSITY
// ============================================
// A named pipeline without a catch-all fails
// when the prefix is used on a file that does not
// match the specific glob.
// The jsx:*.svg entry handles SVG files.
// The jsx:* entry handles everything else.
// Without jsx:*, a jsx: import of a .js file fails.
// ============================================
// PART 9: THE VERIFICATION
// ============================================
// [ ] The named pipeline is defined in .parcelrc.
// [ ] The "..." runs the default transformer first.
// [ ] The catch-all entry exists for the prefix.
// [ ] The prefix is not a reserved URL scheme.
// [ ] The import statement uses the prefix.
// [ ] The TypeScript declaration exists for the prefix.
// [ ] The build succeeds and the output is correct.
// ============================================
// PART 10: THE CHECKLIST
// ============================================
// [ ] The specific glob is listed before the general glob.
// [ ] The pipeline has only one "...".
// [ ] The custom transformer is installed.
// [ ] The import uses the prefix.
// [ ] The default import still works.
The ten parts covered the configuration, the import, the TypeScript declaration, the optimizer inheritance, the reserved prefix, the "..." position, the single-spread rule, the catch-all, and the verification. The through-line is that the prefix is the selector, and the .parcelrc is the chain.
Quick Reference
Named pipeline definition
| Element | Example | Purpose |
|---|---|---|
| Specific glob | "jsx:*.svg" | The pipeline for SVG files with the jsx: prefix |
| Catch-all | "jsx:*" | The fallback for other files with the prefix |
"..." | ["...", "plugin"] | Runs the default chain first |
| Import | import Icon from 'jsx:./icon.svg' | Selects the pipeline |
The "..." position
| Form | Order |
|---|---|
["...", "custom"] | Default first, then custom |
["custom", "..."] | Custom first, then default |
["custom1", "...", "custom2"] | Custom1, default, custom2 |
The constraints
| Constraint | Rule |
|---|---|
| Prefix | Not a reserved URL scheme (http:, npm:, etc.) |
| Spread | Exactly one "..." per pipeline |
| Catch-all | Required for the prefix if non-matching files may use it |
Best Practices
โ Do This:
// Define the specific pipeline and the catch-all
"jsx:*.svg": ["...", "@parcel/transformer-svg-jsx"], // โ
"jsx:*": ["..."] // โ
// Put "..." before the custom transformer
"jsx:*.svg": ["...", "@parcel/transformer-svg-jsx"] // โ
// Use a non-reserved prefix
"myproto:*": ["my-transformer"] // โ
โ Don’t Do This:
// Use a reserved prefix
"http:*": ["my-transformer"] // reserved scheme // โ
// Use two spreads
"jsx:*.svg": ["...", "custom", "..."] // ambiguous // โ
// Omit the catch-all
"jsx:*.svg": ["..."] // no "jsx:*" entry // โ
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| The named pipeline is not found | The prefix does not match the glob | Check the prefix in the import and the .parcelrc |
| The default transformer is not running | "..." is missing | Add "..." to the pipeline |
The build fails on a non-SVG jsx: import | The catch-all is missing | Add "jsx:*": ["..."] |
| The build throws a reserved pipeline error | The prefix is a reserved scheme | Use a custom prefix |
| The build throws a spread error | Two "..." in the pipeline | Use one "..." |
Real-World Examples
1. The jsx: SVG pipeline
"jsx:*.svg": ["...", "@parcel/transformer-svg-jsx"]
2. The catch-all
"jsx:*": ["..."]
3. The import
import Icon from 'jsx:./icon.svg';
4. The TypeScript declaration
declare module "jsx:*.svg" { const C: React.FC; export default C; }
5. The arraybuffer: pipeline
"arraybuffer:*": ["...", "parcel-transformer-arraybuffer"]
6. The bundle-text: import
import css from 'bundle-text:./styles.css';
7. The single spread
"jsx:*.svg": ["...", "custom"] // not ["...", "custom", "..."]
8. The reserved prefix error
"http:*": [] // throws
9. The optimizer inheritance
// Optimizers inherit the pipeline from the bundle's entry asset
10. The default import
import url from './icon.svg'; // still works
Visual
The Named Pipeline Selection
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ THE PREFIX SELECTS THE CHAIN โ
โ โ
โ import Icon from 'jsx:./star.svg' โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Match "jsx:*.svg" โ โ
โ โโโโโโโโโโโโฌโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ "..." โ default SVG โ Parses the SVG โ
โ โ transformer โ โ
โ โโโโโโโโโโโโฌโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ @parcel/transformer โ Converts to React component โ
โ โ -svg-jsx โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ import starUrl from './star.svg' โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Default "*.svg" โ Returns a URL string โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The "..." Position
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ WHERE "..." SITS DETERMINES THE ORDER โ
โ โ
โ ["...", "custom"] โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Default runs first โ โ
โ โ Custom runs second โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ ["custom", "..."] โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Custom runs first โ โ
โ โ Default runs second โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ The position is the order. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Catch-All Necessity
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ WITHOUT THE CATCH-ALL โ
โ โ
โ "jsx:*.svg": ["...", "custom"] โ
โ โ
โ import Icon from 'jsx:./icon.svg' โ matches, works โ
โ import X from 'jsx:./file.js' โ no match, fails โ
โ โ
โ WITH THE CATCH-ALL โ
โ โ
โ "jsx:*.svg": ["...", "custom"], โ
โ "jsx:*": ["..."] โ
โ โ
โ import Icon from 'jsx:./icon.svg' โ specific, works โ
โ import X from 'jsx:./file.js' โ catch-all, works โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| Definition | A transformer glob with a URL-scheme prefix |
| Selection | The prefix in the import statement |
"..." | Runs the default chain at the position it appears |
| Catch-all | Required for the prefix if non-matching files may use it |
| Reserved prefixes | node:, npm:, http:, https:, data:, tel:, mailto: |
| Spread limit | Exactly one "..." per pipeline |
| Optimizer inheritance | From the bundle’s entry asset |
Key takeaways:
- A named pipeline is a prefix on a transformer glob. The prefix becomes an import scheme, and the import statement selects the chain. The same file can be processed in multiple ways depending on how it is imported .
- The
"..."runs the default chain first. Without it, the custom transformer replaces the default chain and may fail because it receives the wrong input. The position of"..."determines the order of the default and custom plugins . - The catch-all is required for the prefix. A
jsx:*.svgentry handles SVG files, but ajsx:import of a non-SVG file needs ajsx:*entry. Without it, the import fails . - The prefix must not be a reserved URL scheme. Parcel reserves
node:,npm:,http:,https:,data:,tel:, andmailto:for its own resolution. A pipeline key with one of these prefixes throws a “reserved pipeline” error . - A pipeline can contain only one
"...". Two spreads are ambiguous: which one inserts the default chain? The build throws an error if a pipeline has more than one spread .
Remember: A named pipeline is a choice that the import statement makes. The .parcelrc defines the chain, and the prefix in the import selects it. The "..." is the mechanism that preserves the default behavior while inserting the custom plugin. The catch-all is the safety net for the prefix. The reserved prefixes are the constraint. The named pipeline is the tool for the case where a glob is not enoughโwhen the same file needs to be a URL in one place and a component in another, and the build must produce both.
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!