Stand with Ukraine 🇺🇦
Eleventy
The possum is Eleventy’s mascot

Eleventy Documentation

Menu

WebC

Template Languages:

Logo for WebC

Type Value
Eleventy Name webc
File Extension *.webc
npm @11ty/webc and @11ty/eleventy-plugin-webc
GitHub 11ty/webc and 11ty/eleventy-plugin-webc

Table of Contents

Why use WebC? Jump to heading

Resources Jump to heading

Installation Jump to heading

Note that WebC support in Eleventy is not bundled with core! You must install the officially supported Eleventy plugin and the plugin requires Eleventy 2.0.0-canary.16 or newer.

It’s on npm at @11ty/eleventy-plugin-webc!

npm install @11ty/eleventy-plugin-webc

To add support for .webc files in Eleventy, add the plugin in your Eleventy configuration file:

Filename .eleventy.js
const pluginWebc = require("@11ty/eleventy-plugin-webc");

module.exports = function(eleventyConfig) {
eleventyConfig.addPlugin(pluginWebc);
};

You’re only allowed one module.exports in your configuration file. If you already have a configuration file, only copy the require and the addPlugin lines above!

Full options list (defaults shown)
const pluginWebc = require("@11ty/eleventy-plugin-webc");

module.exports = function(eleventyConfig) {
eleventyConfig.addPlugin(pluginWebc, {
// Glob to find no-import global components
// (The default changed from `false` in Eleventy WebC v0.7.0)
components: "_components/**/*.webc",

// Adds an Eleventy WebC transform to process all HTML output
useTransform: false,

// Additional global data used in the Eleventy WebC transform
transformData: {},
});
};

Usage Jump to heading

There are a few different ways to use WebC in Eleventy:

  1. Add a new .webc file to your Eleventy input directory
  2. Use the Render plugin in an existing non-WebC template
  3. Pre-process HTML input as WebC
  4. Post-process HTML output as WebC

Add a new .webc file Jump to heading

Adding the plugin will enable support for .webc files in your Eleventy project. Just make a new .webc HTML file in your Eleventy input directory and Eleventy will process it for you! Notably, .webc files will operate WebC in bundler mode, aggregating the CSS and JS in use on each individual page to create a bundle of the assets in use on the page.

WebC uses an HTML parser to process input files: use any HTML here!

Filename my-page.webc
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>WebC Example</title>
</head>
<body>
WebC *is* HTML.
</body>
</html>

Use the Render plugin Jump to heading

Using Eleventy’s built-in Render plugin allows you to render WebC inside of an existing Liquid, Nunjucks, or 11ty.js template.

View this example in: Liquid Nunjucks 11ty.js Handlebars
Syntax Liquid
{% renderTemplate "webc" %}
<my-custom-component></my-custom-component>
{% endrenderTemplate %}
Syntax Nunjucks
{% renderTemplate "webc" %}
<my-custom-component></my-custom-component>
{% endrenderTemplate %}
Syntax JavaScript
module.exports = async function() {
let content = await this.renderTemplate(`<my-custom-component></my-custom-component>`, "webc");
return content;
};

The renderTemplate shortcode requires an async-friendly template language and is not available in Handlebars.

Pre-process HTML input as WebC Jump to heading

You can use the configuration option to change the default HTML preprocessor (from liquid) to webc. This might look like htmlTemplateEngine: "webc". Read more on the Eleventy documentation: Default Template Engine for HTML Files.

Post-process HTML output as WebC Jump to heading

This is a (last-resort?) catch-all option to let WebC process .html output files in your project (skipping any .webc input files to avoid double-processing templates). This feature makes use of Eleventy transforms and is most useful when you want to get up and running with WebC on an existing project quickly.

A few drawbacks to the transform method:

  1. This is the slowest build-performance method to implement WebC in a project, so try the other methods first!
  2. The WebC Eleventy transform operates with bundler mode disabled, which means that processes WebC but does not aggregate component JS or CSS.
The transform is disabled by default, you will need to use the useTransform option to enable it.
const pluginWebc = require("@11ty/eleventy-plugin-webc");

module.exports = function(eleventyConfig) {
eleventyConfig.addPlugin(pluginWebc, {
useTransform: true,
});
};

WebC Reference Jump to heading

Note that all webc: attributes are removed from the rendered output HTML.

HTML-only components Jump to heading

When a component has only content HTML (no CSS or JavaScript) it will ignore the host component tag in the output HTML. This enables HTML-only components to have zero overhead HTML. (You can opt-out of this behavior with webc:keep.)

Expand for Example
WebC components are not limited to custom element name restrictions (e.g. my-component) here. You can use p, blockquote, h1, img, or any valid HTML tag name.
Filename page.webc
<!doctype html>
<title>WebC Example</title>
<my-component></my-component>
Filename components/my-component.webc
Components don’t need a root element, y’all.

Outputs:

Filename _site/page.html
<!doctype html>
<html>
<head>
<title>WebC Example</title>
</head>
<body>
Components don’t need a root element, y’all.
</body>
</html>

Asset bundling Jump to heading

For components that are not HTML-only (they do have CSS or JS), WebC will include the component tag in the output markup (e.g. <my-component>) (for styling or client scripting). (You can opt-out of this behavior with webc:nokeep.)

Expand for Example
WebC components are not limited to custom element name restrictions (e.g. my-component) here. You can use p, blockquote, h1, img, or any valid HTML tag name.
Filename page.webc
<!doctype html>
<title>WebC Example</title>
<my-component></my-component>
Filename components/my-component.webc
Components don’t need a root element, y’all.
<style>/* Hi */</style>

Outputs:

Filename _site/page.html
<!doctype html>
<html>
<head>
<title>WebC Example</title>
</head>
<body>
<my-component>Components don’t need a root element, y’all.</my-component>
</body>
</html>

Eleventy runs WebC in Bundler mode. That means that when it finds <style>, <link rel="stylesheet">, or <script> elements in component definitions they are removed from the output markup and their content is aggregated together for re-use in asset bundles on the page. Read more about CSS and JS in WebC. (You can opt-out of this behavior with webc:keep.)

webc:keep Jump to heading

With an HTML-only component, you can use webc:keep on the host component to keep the tag around:

<html-only-component webc:keep></html-only-component>

You can also use webc:keep to opt-out of asset bundling for individual elements inside of a component definition:

<style webc:keep></style>
<script webc:keep></script>

You can also use webc:keep to save a <slot> for use in a client-side custom element.

webc:nokeep Jump to heading

With an CSS/JS component (not an HTML-only component), you can use webc:nokeep on the host component to drop the tag:

<css-js-component webc:nokeep></css-js-component>

webc:import Jump to heading

WebC will expand any component it finds using known components. You can also use webc:import to inline import a component definition. This import path is relative to the component file path. WebC checks for circular component dependencies and throws an error if one is encountered.

<any-tag-name webc:import="./components/my-component.webc"></any-tag-name>

New in @11ty/webc@0.6.2You can import directly from an installed npm package. Eleventy will begin to supply WebC components with existing plugins. The Syntax Highlighter (4.2.0 or newer) supplies one that you can use today:

<syntax-highlight language="js" webc:import="npm:@11ty/eleventy-plugin-syntaxhighlight">
function myFunction() {
return true;
}
</syntax-highlight>

This uses the component tag name (syntax-highlight) to look for a WebC component at node_modules/@11ty/eleventy-plugin-syntaxhighlight/syntax-highlight.webc and imports it for use on this node. This works with a tag name override via webc:is too.

webc:if Jump to heading

(WebC v0.7.1+)

Use webc:if to conditionally render elements. Accepts arbitrary JavaScript (and is async-friendly). Similar to dynamic attributes, this also has access to component attributes and properties.

<div webc:if="true">This will render</div>
<div webc:if="false">This will not render</div>
<div webc:if="myAsyncHelper()">If the helper promise resolves to a truthy value, this will render</div>

For more complex conditionals, webc:type="js" (WebC v0.7.1+) is recommended (read more below).

Slots Jump to heading

Child content optionally precompiles using <slot> and [slot] too. This example is using an HTML-only component.

Filename page.webc
<my-component></my-component>
<my-component>This is the default slot</my-component>
Filename components/my-component.webc
<p><slot>Fallback slot content</slot></p>

Compiles to:

<p>Fallback slot content</p>
<p>This is the default slot</p>

If your WebC component wants to output a <slot> tag in the compiled markup (for use in client JavaScript), use the webc:keep attribute (e.g. <slot webc:keep>).

Per web component standard conventions, if your component file contains no content markup (e.g. empty or only <style>/<script>), <slot></slot> is implied and the default slot content will be included automatically. If the WebC component file does contain content markup, the content passed in as the default slot requires <slot> to be included.

Named slots Jump to heading

This works with named slots (e.g. <span slot="named-slot">) too.

Expand for Example
Filename page.webc
<my-component>
This is the default slot.
<strong slot="named-slot">This is a named slot</strong>
This is also the default slot.
</my-component>
Filename components/my-component.webc
<p><slot name="named-slot"></slot></p>

Compiles to:

<p><strong>This is a named slot.</strong></p>

Attributes and webc:root Jump to heading

Filename page.webc
<my-component class="sr-only"></my-component>

Inside of your component definition, you can add attributes to the outer host component using webc:root:

Filename components/my-component.webc
<template webc:root class="another-class">
Some component content
</template>

class and style attribute values are merged as expected between the host component and the webc:root element.

Override the host component tag Jump to heading

You can use webc:root webc:keep together to override the host component tag name! This isn’t very useful for HTML-only components (which leave out the host component tag) but is very useful when your component has style/scripts.

Filename components/my-component.webc
<button webc:root webc:keep>Some component content</button>
<style>/* Hi */</style>

Props (Properties) Jump to heading

Make any attribute into a prop by prefixing it with @. Props are “private” attributes that don’t end up in the output HTML (they are private to WebC). They are identical to attributes except that they are filtered from the output HTML.

Filename page.webc
<my-component @prop="Hello"></my-component>
Filename components/my-component.webc
<p @text="prop"></p>
<!-- outputs <p>Hello</p> -->

Dynamic attributes Jump to heading

Make any attribute into a dynamic attribute by prefixing it with :. You have access to host component attributes, props, and page data here!

Filename page.webc
<avatar-image src="my-image.jpeg" alt="Zach is documenting this project"></avatar-image>
Filename components/avatar-image.webc
<img :src="src" :alt="alt" class="avatar-image">

@html Jump to heading

We surface a special @html prop to override any tag content with custom JavaScript.

<template @html="'Template HTML'"></template>
<template @html="dataProperty"></template>
<!-- webc:nokeep will replace the outer element -->
<template @html="'Template HTML'" webc:nokeep></template>
<!-- No reprocessing as WebC (useful in Eleventy layouts) -->
<!-- Where `myHtmlContent` is a variable holding an arbitrary HTML string -->
<template @raw="myHtmlContent" webc:nokeep></template>

@raw Jump to heading

New in @11ty/webc@0.7.1

As noted in @html, you can use @raw as an alias for webc:raw @html.

@text Jump to heading

New in @11ty/webc@0.6.0

We provide a special @text prop to override any tag content with custom JavaScript. The entire value returned here will be escaped!

<p @text="dataProperty"></p>

<!-- When dataProperty contains `<p>This is text</p>`, this renders: -->
<p>&lt;p&gt;This is text&lt;/p&gt;</p>
<!-- webc:nokeep will replace the outer element -->
<p @text="dataProperty" webc:nokeep></p>

webc:is Jump to heading

Remap a component to another component name.

<div webc:is="my-component"></div>

<!-- equivalent to -->
<my-component></my-component>

webc:scoped Jump to heading

We include a lightweight mechanism (webc:scoped) to scope component CSS. Selectors are prefixed with a new component class name. The class name is based on a hash of the style content (for fancy de-duplication of identical component styles).

Expand for example
Filename page.webc
<my-component>Default slot</my-component>

If you use :host it will be replaced with that class selector.

Filename components/my-component.webc
<style webc:scoped>
:host {
color: blue;
}
:host:defined {
color: rebeccapurple;
}
</style>

This outputs:

<my-component class="wcl2xedjk">Default slot</my-component>

and aggregates the following CSS to the bundle:

.wcl2xedjk{color:blue}
.wcl2xedjk:defined{color:rebeccapurple}

webc:type Jump to heading

Adding your own Custom Transform directly to WebC is not yet available in the Eleventy WebC plugin! If this is something folks would like to see added, please let us know! Do note that you can add your own custom template engine to the render plugin!

webc:type="11ty" Jump to heading

The Custom Transforms feature (e.g. webc:type) in the Eleventy WebC plugin has been wired up to the Eleventy Render plugin to allow you to use existing Eleventy template syntax inside of WebC.

Note that the webc:type="11ty" feature is exclusive to the Eleventy WebC plugin and is not available in non-Eleventy independent WebC.

Use webc:type="11ty" with the 11ty:type attribute to specify a valid template syntax.

Filename my-page.webc
---
frontmatterdata: "Hello from Front Matter"
---
<template webc:type="11ty" 11ty:type="liquid,md">
{% assign t = "Liquid in WebC" %}
## {{ t }}

_{{ frontmatterdata }}_
</template>

webc:type (JavaScript Render Functions) Jump to heading

You can also transform individual element content using webc:type. In addition to webc:type="11ty" above, there are three more bundled types:

JavaScript Render Functions are async friendly (e.g. async function()):

webc:type="js" Jump to heading

New in @11ty/webc@0.7.1 Run any arbitrary server JavaScript in WebC. Outputs the result of the very last statement executed in the script. Async-friendly (return a promise and we’ll resolve it).

Filename page.webc
<img src="my-image.jpeg" alt="An excited Zach is trying to finish this documentation">
Filename components/img.webc
<script webc:type="js">
if(!alt) {
throw new Error("oh no you didn’t");
}
`<img src="${src}" alt="${alt}">`;
</script>
Expand to see this example with webc:type="render"
<script webc:type="render">
function() {
if(!this.alt) {
throw new Error("oh no you didn’t");
}
// Free idea: use the Eleventy Image plugin to return optimized markup
return `<img src="${this.src}" alt="${this.alt}">`;
}
</script>

Or use a JavaScript render function to generate some CSS:

Filename page.webc
<style webc:is="add-banner-to-css" license="MIT licensed">
p { color: rebeccapurple; }
</style>
Filename components/add-banner-to-css.webc
<script webc:type="js" webc:is="style">`/* ${license} */`</script>
<slot></slot>
Expand to see this example with webc:type="render"
<script webc:type="render" webc:is="style">
function() {
return `/* ${this.license} */`;
}
</script>
<slot></slot>

Bonus tips:

Here’s another example of a more complex conditional (you can also use webc:if!):

<script webc:type="js">
if(alt) {
`<img src="${src}" alt="${alt}">`
} else {
`<a href="${src}">Your image didn’t have an alt so you get this link instead.</a>`
}
</script>

webc:raw Jump to heading

Use webc:raw to opt-out of WebC template processing for all child content of the current node. Notably, attributes on the current node will be processed. This works well with <template>!

Filename components/my-component.webc
<template webc:raw>
Leave me out of this.
<style>
p { color: rebeccapurple; }
</style>
</template>

Helper Functions Jump to heading

WebC Helpers are JavaScript functions available in dynamic attributes, @html, @raw, and render functions.

Eleventy-provided Helpers Jump to heading

New in @11ty/eleventy-plugin-webc@0.5.0Included with Eleventy WebC, JavaScript template functions and Universal Filters are provided automatically as WebC Helpers.

This includes url, slugify, log and others!

<!-- Use the  Eleventy provided `url` universal filter -->
<a :href="url('/local-path/')">My Link</a>

Supply your own Helper Jump to heading

Filename .eleventy.js
module.exports = function(eleventyConfig) {
// via Universal Filter
eleventyConfig.addFilter("alwaysRed", () => "Red");

// or via JavaScript Template Function directly
eleventyConfig.addJavaScriptFunction("alwaysBlue", () => "Blue");

// Don’t forget to add the WebC plugin in your config file too!
};
<div @html="alwaysRed()"></div>
<div @html="alwaysBlue()"></div>

<!-- renders as: -->
<div>Red</div>
<div>Blue</div>

Subtleties and Limitations Jump to heading

Void elements Jump to heading

Custom elements (per specification) are not supported as void elements: they require both a starting and ending tag. You can workaround this limitation using webc:is.

<head> Components Jump to heading

There are a few wrinkles when using an HTML parser with custom elements. Notably, the parser tries to force custom element children in the <head> over to the <body>. To workaround this limitation, use webc:is.

Expand for a few example workarounds
<head webc:is="my-custom-head">
<!-- this is slot content, yes you can use named slots here too -->
</head>
<head>
<!-- <my-custom-head> is not allowed here -->
<meta webc:is="my-custom-head">
<title webc:is="my-custom-title">Default Title</title>
</head>

Rendering Modes Jump to heading

There are two different rendering modes in Eleventy: page and component. We attempt to guess the rendering mode that you’d like based on the markup you supply. The page rendering mode is for rendering full HTML pages. The component rendering mode is for fragments of HTML. Most of the time you won’t need to worry about this distinction but it is included in the documentation for completeness.

Eleventy + WebC Features Jump to heading

Front Matter Jump to heading

WebC in Eleventy works automatically with standard Eleventy conventions for front matter (though front matter in Eleventy is optional).

Filename with-front-matter.webc
---
layout: "my-layout.webc"
---
WebC *is* HTML.
Expand to see an example my-layout.webc

The above example assumes the existence of _includes/my-layout.webc (an Eleventy layout).

Filename _includes/my-layout.webc
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>WebC Example</title>
</head>
<body @raw="content"></body>
</html>

WebC versions prior to 0.5.0 required this. (e.g. this.content) when referencing data/attributes/property values. This is no longer required when using @html.

Notable note: front matter (per standard Eleventy conventions) is supported in page-level templates only (.webc files in your input directory) and not in components (see below).

Defining Components Jump to heading

Components are the magic of WebC and there are a few ways to define components in WebC:

  1. Use global no-import components specified in your config file.
  2. Specify a glob of no-import components at a directory or template level in the data cascade.
  3. You can use webc:import inside of your components to import another component directly.
Notably, WebC components can have any valid HTML tag name and are not restricted to the same naming limitations as custom elements (they do not require a dash in the name).

Global no-import Components Jump to heading

Use the components property in the options passed to addPlugin in your Eleventy configuration file to specify project-wide WebC component files available for use in any page.

const pluginWebc = require("@11ty/eleventy-plugin-webc");

module.exports = function(eleventyConfig) {
eleventyConfig.addPlugin(pluginWebc, {
// Glob to find no-import global components
// This path is relative to the project-root!
// (The default changed from `false` in Eleventy WebC v0.7.0)
components: "_components/**/*.webc",
});
};

Notably, the path for components is relative to your project root (not your project’s input directory).

The file names of components found in the glob determine the global tag name used in your project (e.g. _includes/components/my-component.webc will give you access to <my-component>).

Declaring Components in Front Matter Jump to heading

You can also use and configure specific components in front matter (or, via any part of the data cascade—scoped to a folder or a template) by assigning a glob (or array of globs) to the property at webc.components:

Filename my-directory/my-page.webc
---
layout: "my-layout.webc"
webc:
components: "./webc/*.webc"
---
<my-webc-component>WebC *is* HTML.</my-webc-component>

By default these paths are relative to the template file. If you’re setting this in the data cascade in a directory data file that will apply multiple child folders deep, it might be better to:

  1. Use the global no-import components option.
  2. Use ~/ as a prefix (e.g. ~/my-directory/webc/*.webc) to alias to the project’s root directory.

CSS and JS (Bundler mode) Jump to heading

Eleventy Layouts can bundle any specific page’s assets (CSS and JS used by components on the page). These are automatically rolled up when a component uses <script>, <style>, or <link rel="stylesheet">. You can use this to implement component-driven Critical CSS.

Note that if a <style> is nested inside of declarative shadow root template (e.g. <template shadowroot>), it is left as is and not aggregated.
Filename _includes/webc/my-webc-component.webc
<style>/* This is component CSS */</style>
<script>/* This is component JS */</script>

<!-- File references work too -->
<link rel="stylesheet" href="my-file.css">
<script src="my-file.js"></script>

As shown above this also includes <link rel="stylesheet"> and <script src> when the URLs point to files on the file system (remote URL sources are not yet supported).

You can opt-out of bundling on a per-element basis using webc:keep.

Filename _includes/layout.webc
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>WebC Example</title>
<style @raw="getCss(page.url)"></style>
<script @raw="getJs(page.url)"></script>
</head>
<body @raw="content"></body>
</html>

@raw was New in @11ty/webc@0.7.1. Previous versions can use webc:raw @html.

Make sure you’re using these getCss and getJs helpers in an Eleventy Layout file.

WebC versions prior to 0.5.0 required this. (e.g. this.getCss/this.page.url) when referencing helpers/data/attributes/property values. This is no longer required when using @html.

Outside of *.webc files (e.g. in a Nunjucks or Liquid layout file), the Eleventy WebC plugin also publishes two universal filters webcGetCss and webcGetJs, for use like this:

Filename _includes/layout.njk
<style>{{ page.url | webcGetCss | safe }}</style>
<script>{{ page.url | webcGetJs | safe }}</script>
Filename _includes/layout.liquid
<style>{{ page.url | webcGetCss }}</style>
<script>{{ page.url | webcGetJs }}</script>

Asset bucketing Jump to heading

Components can use the webc:bucket feature to output to any arbitrary bucket name for compartmentalization at the component level.

Filename _includes/webc/my-webc-component.webc
<style>/* This CSS is put into the default bucket */</style>
<script>/* This JS is put into the default bucket */</script>
<style webc:bucket="defer">/* This CSS is put into the `defer` bucket */</style>
<script webc:bucket="defer">/* This JS is put into the `defer` bucket */</script>
Filename _includes/layout.webc
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>WebC Example</title>
<!-- Default bucket -->
<style @raw="getCss(page.url)"></style>
<script @raw="getJs(page.url)"></script>
</head>
<body>
<template webc:nokeep @raw="content"></template>

<!-- `defer` bucket -->
<style @raw="getCss(page.url, 'defer')"></style>
<script @raw="getJs(page.url, 'defer')"></script>
</body>
</html>

WebC versions prior to 0.5.0 required this. (e.g. this.getCss/this.page.url) when referencing helpers/data/attributes/property values. This is no longer required when using @html.

Use with is-land Jump to heading

You can also use this out of the box with Eleventy’s is-land component for web component hydration.

At the component level, components can declare their own is-land loading conditions.

Filename _includes/webc/my-webc-component.webc
<is-land on:visible>
<template data-island>
<!-- CSS -->
<style webc:keep>
/* This is on-visible CSS */
</style>
<link rel="stylesheet" href="some-arbitrary-css.css" webc:keep>

<!-- JS -->
<script type="module" webc:keep>
console.log("This is on-visible JavaScript");
</script>
<script type="module" src="some-arbitrary-js.js" webc:keep></script>
</template>
</is-land>

Other pages in Template Languages: