11ty/webc for Single File Web Components
Single File Web Components
At a glance
- What is it?
- 11ty/webc is an ESM JavaScript library that compiles HTML-like components into server-rendered markup, with optional CSS and JavaScript aggregation. It suits framework-independent sites and Eleventy, Express or Koa projects, but the current package is a beta release.
- Who is it for?
- Adopt 11ty/webc when a project needs framework-independent HTML components, server-rendered output and integrations with Eleventy, Express or Koa. Do not adopt it as a stable dependency without reviewing the beta package and confirming that its parsing, aggregation and streaming behavior meets the application requirements.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why 11ty/webc exists
11ty/webc addresses a specific composition problem: keeping a component's markup, styles and scripts in one file while producing ordinary HTML for a server to send. The README describes it as a framework-independent standalone HTML serializer for generating markup for Web Components. Its central convention is that a component can be written as HTML, without a framework-specific top-level template wrapper or a mandatory root element.
The intended users include developers building Web Components, static sites, server-rendered applications and integrations for existing tools. The library is not limited to custom element names. A component definition can remap an ordinary tag such as `img`, making it possible to apply one definition consistently across a project. The repository also lists integrations for Eleventy, Express and Koa. That makes WebC relevant both as a direct JavaScript API and as infrastructure beneath a site generator or web server.
WebC's output is primarily a compilation result, not a browser-side component runtime. The README says Web Components created with WebC can support server-side rendering without duplicating author-written markup. A project using this approach therefore has to understand component registration, compilation conventions and asset aggregation. It is not simply a collection of JavaScript classes sent to the browser and rendered after page load.
How WebC parses and expands component files
The direct API starts with `new WebC()`. Input can be a file selected with `setInputPath`, or a string supplied through `setContent`. The documented `compile()` call returns `html`, `css`, `js` and `components`. A separate `stream()` method returns readable streams for `html`, `css` and `js`, which gives an integration a path for progressive output.
WebC uses `parse5` to parse input according to modern browser parsing conventions. The README says a doctype is optional when content starts with `<html`, because WebC adds one automatically. It also says WebC throws a helpful error when it encounters markup that would trigger quirks mode. This is a meaningful constraint for legacy templates, generated fragments and content pipelines that need permissive handling of malformed documents.
Components can be registered globally with `defineComponents()`. The API accepts a glob, an array of filenames or an object mapping component names to files. After registration, a page can use `<my-component></my-component>`. WebC expands that element with the contents of the associated component file, even when the component has no single root element. It can also import a component directly with `webc:import`. Related conventions include `webc:is` for remapping, `webc:keep` for retaining host markup and the documented slot mechanism.
The repository organizes examples around imports, attributes, properties, lookup attributes, helpers, raw output, rendering, slots, streaming, scoped CSS and aggregation. These directories provide a more concrete map of the API than a framework-specific wrapper would. They also show that a WebC file remains HTML and can include normal document structure.
Bundler mode changes CSS and JavaScript handling
WebC can collect component-level CSS and JavaScript, but that behavior depends on `page.setBundlerMode(true)`. Bundler mode is disabled by default in the direct JavaScript API and enabled by default in the Eleventy WebC plugin, according to the README.
When bundler mode is active, WebC removes `<style>` and `<script>` elements from individual component markup and aggregates those assets for reuse elsewhere. The returned `css` and `js` values can then be written to files or passed into another layout. The README also presents critical CSS as a possible use for the collected stylesheet. A direct user is responsible for deciding where the resulting assets appear in the final document.
The aggregation mechanism includes stylesheet links and external scripts. The README specifically identifies `<link rel="stylesheet">` and `<script src>` in this discussion. This is more than simple inline-style extraction because it gives the caller a chance to combine component assets with a site's broader delivery strategy.
WebC also offers an opt-in CSS prefixer for scoping component CSS. Alternatively, the documentation points to browser-native Shadow DOM style scoping as a future-compatible option as Declarative Shadow DOM support becomes ubiquitous. That is a design choice rather than an automatic equivalence between a build-time prefixer and browser behavior. Projects with strict CSP rules, existing asset hashing or custom CSS processing should inspect the `aggregation` and `scoped-css` examples before selecting a mode.
Installing @11ty/webc and compiling a first file
The README says users who are not building a plugin or integration can probably skip the installation section. The npm package is named `@11ty/webc`, and the package metadata requires Node.js `>=22`. Install it in the project environment with the documented command:
npm install @11ty/webcThe package is ESM. Its `package.json` contains `"type": "module"`, and JavaScript files can import the documented class directly:
import { WebC } from "@11ty/webc";Create a file named `page.webc`, then compile it through the direct API. This example enables bundler mode before reading the file:
import { WebC } from "@11ty/webc";
let page = new WebC();
page.setBundlerMode(true);
page.setInputPath("page.webc");
let { html, css, js, components } = await page.compile();A project can compile a string instead by calling `setContent()`, which is useful when content already exists in memory. The README also documents a dynamic import for CommonJS files:
(async function() {
const { WebC } = await import("@11ty/webc");
})();A CommonJS project should not assume that `require()` is the intended interface. The package requires ESM support through `"type": "module"` or the `.mjs` extension.
Registering global components and selecting an integration
Before compiling a page that references a component, register its file with `defineComponents()`. The documented array form uses each file name as the component name:
import { WebC } from "@11ty/webc";
let page = new WebC();
page.defineComponents(["components/my-component.webc"]);
page.setInputPath("page.webc");
let { html, css, js, components } = await page.compile();The page can contain `<my-component></my-component>`. Compilation expands it using the content of `components/my-component.webc`. The same API also accepts a glob or an object with explicit component-name mappings. That explicit mapping is the safer choice when a filename and desired HTML element name differ.
For application delivery, the repository lists `@11ty/eleventy-plugin-webc` for Eleventy, `express-webc` for Express and `koa-webc` for Koa. These integrations can reduce application-specific work by fitting WebC into an existing build or request pipeline. The README does not claim that each integration has the same asset handling or API surface. A project should inspect the integration repository before assuming that core `compile()` output is inserted automatically.
The direct API is also a realistic choice for a custom server. In that case, the application owns component paths, final HTML placement, CSS and JavaScript output, and stream error handling. This control is useful for unusual architectures, but it is also an integration burden.
Streaming, beta status and upgrade obligations
The `stream()` method is intended for projects that need progressive delivery. It returns readable streams for HTML, CSS and JavaScript, while `compile()` waits for a result object. A streaming integration can avoid holding the complete compiled output in one value, but the README does not define stream ordering, cancellation or error propagation. Those details should be checked in the relevant API or server integration before production use.
The package version is `0.12.0-beta.2`. The release list also includes `v0.12.0-beta.1` and the earlier stable `v0.11.3`. A beta designation does not make the package unusable, but it does mean API and release expectations should be reviewed more carefully than for a stable major version. Pinning an exact version is particularly relevant if component compilation affects a site's HTML output.
The repository's last push was on 2026-10-14. That date indicates recent repository activity, but it does not establish that the beta API is stable or that every listed integration has the same release cadence. The package metadata declares `"license": "MIT"`. That license permits use and modification under its terms, including preservation of the license and copyright notice where required, but it does not answer questions about compatibility with a particular application or remove upgrade work.
Upgrades may affect parsing, component registration, asset aggregation and integration configuration. The presence of examples for each major feature is useful, but the repository does not provide a migration guide in the documented README excerpt. Teams should review release notes and compare generated HTML before changing a production version.
WebC versus Lit SSR and the wrong use case
The README names Enhance, `wcc` and Lit SSR as similar projects. It also points to Lit SSR integrations for Eleventy, Astro and Rocket. The documented architectural difference is that WebC is a standalone serializer and compiler with a programmatic `WebC` API, while Lit SSR is a server-rendering approach associated with Lit and framework-specific plugins.
WebC is not documented as a Lit renderer, a replacement for custom-element definitions or a browser-side framework runtime. A team already using Lit with Astro or Rocket may prefer the integration already associated with that stack. WebC is more relevant when the project wants HTML-first component files, global component registration and optional asset aggregation without requiring a Lit component model.
It is also the wrong tool when the application only needs to load custom elements in a browser, has no build or server compilation step, or requires quirks-mode HTML. The documented parser rejects quirks-mode markup, and the core workflow revolves around compilation. A project that needs only browser-native custom-element behavior should evaluate a browser component approach instead.
The decision should focus on architecture rather than marketing. Verify whether the project needs file expansion, server-rendered output, streaming, slot handling and aggregated assets. If those requirements are absent, WebC adds a compilation layer without providing the primary behavior the project needs.
Editorial conclusion
Adopt 11ty/webc when a project needs framework-independent HTML components, server-rendered output and integrations with Eleventy, Express or Koa. Do not adopt it as a stable dependency without reviewing the beta package and confirming that its parsing, aggregation and streaming behavior meets the application requirements. Verify first that Node.js 22 or later is available, decide whether bundler mode is appropriate, and inspect the selected integration's handling of component assets.
Frequently asked questions
What is 11ty/webc used for?
11ty/webc compiles HTML-like single-file components into server-rendered markup. It expands registered component files and can aggregate component CSS and JavaScript.
How do I install and import 11ty/webc?
Install it with `npm install @11ty/webc`. The package is ESM and requires Node.js `>=22`; the documented import is `import { WebC } from "@11ty/webc";`.
Does 11ty/webc support streaming and CSS aggregation?
Yes. The documented `stream()` method returns readable streams for HTML, CSS and JavaScript. Calling `page.setBundlerMode(true)` enables aggregation of component assets in the direct API.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/11ty-webc)Was this useful?