- Stable
2.0.1
Toggle Menu
5.81s
43.36s
WebC
Contents
- Why use WebC
- Resources
- Installation
- Usage
- WebC Reference
- HTML-only components
- Asset bundling
- webc:keep
- webc:nokeep
- webc:import
- webc:if
- webc:elseif and webc:else
- webc:for Loops
- Slots
- Attributes and webc:root
- Props (Properties)
- Dynamic attributes and properties
- @attributes
- @html
- @raw
- @text
- webc:is
- webc:scoped
- Using JavaScript to Setup your Component
- Using Template Syntax to Generate Content
- Using JavaScript to Generate Content
- webc:raw
- webc:ignore
- Server-only comments
- Custom Transforms
- Helper Functions
- Subtleties and Limitations
- Eleventy + WebC Features
- From the Community
Type | Value |
---|---|
Eleventy Name | webc |
File Extension | *.webc |
npm | @11ty/webc and @11ty/eleventy-plugin-webc |
GitHub | 11ty/webc and 11ty/eleventy-plugin-webc |
Why use WebC?
- Brings first-class components to Eleventy.
- Expand any HTML element (including custom elements) to HTML with defined conventions from web standards.
- This means that Web Components created with WebC are compatible with server-side rendering (without duplicating author-written markup)
- WebC components are Progressive Enhancement friendly.
Performance
- Create streamlined component-driven, cache-friendly page-specific JavaScript and CSS bundles. Users will only load the code they need to render that page (or that island).
- Easily configurable boundaries for critical component CSS and JavaScript.
- Works great with is-land for web component hydration.
- Get first-class incremental builds (for page templates, components, and Eleventy layouts) when used with
--incremental
- Streaming friendly (stream on the Edge π)
Compatible with Standards
- Uses
parse5
to parse WebC HTML as modern browsers do (a nod to @DasSurmaβs work with Vite here) - Shadow DOM and Declarative Shadow DOM friendly (easily switch components between Light DOM and Shadow DOM)
Authoring
- Encourages no-quirks mode HTML authoring (and a doctype is optional). WebC throws a helpful error if encounters quirks mode markup.
- Easily scope component CSS (or use your own scoping utility).
- Tired of importing components? Use global or per-page no-import components.
- Async-friendly: All configuration extensions/hooks into WebC are async-friendly out of the box.
- For more complex templating needs, render any existing Eleventy template syntax (Liquid, markdown, Nunjucks, etc.) inside of WebC.
Resources
- Introduction to WebC (11ty.rocks) by W. Evan Sheehan
- Understanding WebC Features and Concepts (11ty.rocks) by Stephanie Eckles
- WebC Number Counter Example Source Code and Demo
- Seven Demos of Progressive Enhancement using Image Comparison Components and Source Code
- First Experience Building with Eleventy's WebC Plugin
- zachleat.com: Adding Components to Eleventy with WebC: a brief history of the motivation behind WebC including influences from the Svelte and Vue communities.
- 11ty.webc.fun: a collection of WebC recipes!
- Robin Cussol: Optimize your img tags with Eleventy Image and WebC
Installation
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:
import pluginWebc from "@11ty/eleventy-plugin-webc";
export default function(eleventyConfig) {
eleventyConfig.addPlugin(pluginWebc);
};
const pluginWebc = require("@11ty/eleventy-plugin-webc");
module.exports = function(eleventyConfig) {
eleventyConfig.addPlugin(pluginWebc);
};
Full options list (defaults shown)
import pluginWebc from "@11ty/eleventy-plugin-webc";
export default 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: {},
// Options passed to @11ty/eleventy-plugin-bundle
bundlePluginOptions: {},
});
};
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: {},
// Options passed to @11ty/eleventy-plugin-bundle
bundlePluginOptions: {},
});
};
View the full options list for the Bundle plugin. As an example, you can use the transforms
array to modify bundle content with postcss.
Syntax highlighting
Because WebC is HTML you can configure your editor to treat .webc
files as
HTML, this should correctly syntax highlight your WebC files. Your editor of
choice should have some documentation on how to get this working.
Usage
There are a few different ways to use WebC in Eleventy:
Add a new .webc
file
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!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>WebC Example</title>
</head>
<body>
WebC *is* HTML.
</body>
</html>
Non-traditional WebC usage
Use the Render plugin
Using Eleventyβs built-in Render plugin allows you to render WebC inside of an existing Liquid, Nunjucks, or 11ty.js template.
{% renderTemplate "webc" %}
<my-custom-component></my-custom-component>
{% endrenderTemplate %}
{% renderTemplate "webc" %}
<my-custom-component></my-custom-component>
{% endrenderTemplate %}
export default async function () {
let content = await this.renderTemplate(
`<my-custom-component></my-custom-component>`,
"webc"
);
return content;
};
module.exports = async function () {
let content = await this.renderTemplate(
`<my-custom-component></my-custom-component>`,
"webc"
);
return content;
};
Pre-process HTML input as WebC
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
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:
- This is the slowest build-performance method to implement WebC in a project, so try the other methods first!
- The WebC Eleventy transform operates with bundler mode disabled, which means that processes WebC but does not aggregate component JS or CSS. (Upvote this enhancement request)
The transform is disabled by default, you will need to use the useTransform
option to enable it.
import pluginWebc from "@11ty/eleventy-plugin-webc";
export default function (eleventyConfig) {
eleventyConfig.addPlugin(pluginWebc, {
useTransform: true,
});
};
const pluginWebc = require("@11ty/eleventy-plugin-webc");
module.exports = function (eleventyConfig) {
eleventyConfig.addPlugin(pluginWebc, {
useTransform: true,
});
};
WebC Reference
Note: All webc:
attributes are removed from the rendered output HTML.
HTML-only components
- Related: Defining Components in WebC
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
my-component
) here. You can use p
, blockquote
, h1
, img
, or any valid HTML tag name.<!DOCTYPE html>
<title>WebC Example</title>
<my-component></my-component>
Components donβt need a root element, yβall.
Outputs:
<!DOCTYPE html>
<html>
<head>
<title>WebC Example</title>
</head>
<body>
Components donβt need a root element, yβall.
</body>
</html>
Asset bundling
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
my-component
) here. You can use p
, blockquote
, h1
, img
, or any valid HTML tag name.<!DOCTYPE html>
<title>WebC Example</title>
<my-component></my-component>
Components donβt need a root element, yβall.
<style>
/* Hi */
</style>
Outputs:
<!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
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
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
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.
- Related: Defining Components in WebC (global or scoped)
<any-tag-name webc:import="./components/my-component.webc"></any-tag-name>
Added 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
Added in @11ty/webc@0.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>
You can use webc:type="js"
(WebC v0.7.1+) to use JavaScript for more complex conditional logic (read more below).
webc:elseif
and webc:else
Added in @11ty/webc@0.10.0
Adjacent siblings of webc:if
can use webc:elseif=""
and webc:else
for additional conditional logic.
<div webc:if="false">This will not render</div>
<!-- interspersing comments works ok -->
<div webc:elseif="true">This will render</div>
<div webc:else>This will not render</div>
webc:for
Loops
Added in @11ty/webc@0.10.0
Use webc:for
to loop over data with HTML. It works with Objects and any Iterable (String, Array, Map, Set, etc).
The syntax should feel similar to JavaScriptβs for
statement.
Arrays (or any other Iterable)
<!-- renders three div elements -->
<div webc:for="item of [1, 2, 3]" @text="item"></div>
<!-- access the loop index (zero-indexed) -->
<div webc:for="(item, index) of [1, 2, 3]" @text="index"></div>
<!-- name these whatever youβd like -->
<div webc:for="myItem of [1, 2, 3]" @text="myItem"></div>
<div webc:for="(myItem, myIndex) of [1, 2, 3]" @text="myIndex"></div>
<!-- any iterable -->
<div webc:for="item of new Set([1, 2, 3])" @text="item"></div>
Objects
Note the use of in
instead of of
.
<!-- renders two div elements -->
<div webc:for="key in { a: 1, b: 2 }" @text="key"></div>
<!-- access the value -->
<div webc:for="(key, value) in { a: 1, b: 2 }" @text="value"></div>
<!-- access the loop index (zero-indexed) -->
<div webc:for="(key, value, index) in { a: 1, b: 2 }" @text="index"></div>
<!-- name these whatever youβd like -->
<div
webc:for="(myKey, myValue, myIndex) in { a: 1, b: 2 }"
@text="myIndex"
></div>
<!-- use `Object.values` or `Object.keys`, sure -->
<div webc:for="value of Object.values({ a: 1, b: 2 })"></div>
<div webc:for="key of Object.keys({ a: 1, b: 2 })"></div>
Nesting webc:for
Loops can be nested but access to outer scope from the inner loop doesn't work currently. More at issue #175.
Slots
Child content optionally precompiles using <slot>
and [slot]
too. This example is using an HTML-only component.
<my-component></my-component>
<my-component>This is the default slot</my-component>
<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>
).
<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
This works with named slots (e.g. <span slot="named-slot">
) too.
Expand for Example
<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>
<p><slot name="named-slot"></slot></p>
Compiles to:
<p><strong>This is a named slot.</strong></p>
Attributes and webc:root
<my-component class="sr-only"></my-component>
Inside of your component definition, you can add attributes to the outer host component using webc:root
:
<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.
- Attributes: HTML attribute strings.
- Properties: server-only private HTML attribute strings (not rendered to output).
- Dynamic Attributes and Properties: evaluate as JavaScript (any data type, not just strings).
Override the host component tag
You can use webc:root="override"
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.
<button webc:root="override">Some component content</button>
<style>
/* Hi */
</style>
- Added in @11ty/webc@0.9.0Previously, the above used to be accomplished by using
webc:root
andwebc:keep
together on an element.
Nesting
Itβs worth noting also that webc:root
can be nested inside of other contentβit does not need to exist at the top level of the component definition. (Framework folks love things deeply nested in div
s, right?)
<div>
<div>
<template webc:root="override" class="another-class">
Some component content
</template>
</div>
</div>
Props (Properties)
Make any attribute into a prop by prefixing it with @
. Props are server-only β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.
<my-component @prop="Hello"></my-component>
<p @text="prop"></p>
<!-- outputs <p>Hello</p> -->
- In the HTML specification, attribute names are lower-case. Added in @11ty/webc@0.8.0Attribute or property names with dashes are converted to camelcase for JS (e.g.
<my-component @prop-name="test">
can be used like@text="propName"
). More at issue #71.
- Attributes: HTML attribute strings.
- Properties: server-only private HTML attribute strings (not rendered to output).
- Dynamic Attributes and Properties: evaluate as JavaScript (any data type, not just strings).
Dynamic attributes and properties
Make any attribute or property dynamic (using JavaScript for the value instead of a string) by prefixing it with a colon (:
). You have access to host component attributes, props, and page data here!
<avatar-image
src="my-image.jpeg"
alt="Zach is documenting this project"
:@dynamic-prop="'hello'"
></avatar-image>
<img :src="src" :alt="alt" class="avatar-image" />
- Added in @11ty/webc@0.9.0The
:@
dynamic property prefix was added in WebC v0.9.0. - In the HTML specification, attribute names are lower-case. Added in @11ty/webc@0.8.0Attribute or property names with dashes are converted to camelcase for JS (e.g.
<my-component @prop-name="test">
can be used like@text="propName"
). More at #71.
- The only currently supported
webc:*
configuration attribute that supports dynamic values iswebc:bucket
. More to come here: #143 #148
- Attributes: HTML attribute strings.
- Properties: server-only private HTML attribute strings (not rendered to output).
- Dynamic Attributes and Properties: evaluate as JavaScript (any data type, not just strings).
@attributes
Added in @11ty/webc@0.9.0You can use @attributes
to render all of the attributes (including on host component) to the current node.
<!-- will render all attributes including `src` and `alt` from the host component -->
<img @attributes class="avatar-image" />
You can use this to render an arbitrary object as attributes too (note the parentheses to avoid JavaScript parsing as a block
+ label
):
<img @attributes="({ myattribute: 'myValue'})" />
@html
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>
- Content returned from the
@html
prop will be processed as WebCβreturn any WebC content here! Added in @11ty/webc@0.5.0
- Using
webc:raw
will prevent processing the result as WebC Added in @11ty/webc@0.6.0 - Use
@raw
as an alias forwebc:raw @html
Added in @11ty/webc@0.7.1
<!-- 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
Added in @11ty/webc@0.7.1
As noted in @html
, you can use @raw
as an alias for webc:raw @html
.
@text
Added 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><p>This is text</p></p>
<!-- webc:nokeep will replace the outer element -->
<p @text="dataProperty" webc:nokeep></p>
- Content returned from the
@text
prop will not be processed as WebC.
webc:is
Remap a component to another component name.
<div webc:is="my-component"></div>
<!-- equivalent to -->
<my-component></my-component>
webc:scoped
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
<my-component>Default slot</my-component>
If you use :host
it will be replaced with that class selector.
<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;
}
1. The progressive enhancement story requires ubiquitous browser support before using it for content in the critical rendering path.
2. It requires
<style>
duplication in each instance of the component.Just be aware of these tradeoffs. And remember that you can use both methods in WebC!
webc:scoped="my-prefix"
You can also specify an attribute value to webc:scoped
to hard code your own component prefix (e.g. <style webc:scoped="my-prefix">
). This allows the CSS to look a bit more friendly and readable. We will automatically check for duplicate values in your component tree and throw an error if collisions occur.
Using JavaScript to Setup your Component
Added in @11ty/webc@0.9.0You can now also use <script webc:setup>
to run arbitrary JavaScript and provide data and markup to your component. Any top level variables declared here are available in your component as local data.
This is similar to using JavaScript as a custom Eleventy Front Matter type, although data in webc:setup
is scoped to the component and does not flow back up in the Data Cascade.
<script webc:setup>
const myHtml = "<my-webc-component></my-webc-component>";
function alwaysBlue() {
return "blue";
}
</script>
<div @html="myHtml"></div>
<div @raw="myHtml"></div>
<!-- @raw does not reprocess as WebC -->
<div @html="alwaysBlue()"></div>
Works with var
, let
, const
, function
, Array
, and Object
destructuring assignment.
- Uses the
node-retrieve-globals
package.
Using Template Syntax to Generate Content
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.
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.
---
frontmatterdata: "Hello from Front Matter"
---
<template webc:type="11ty" 11ty:type="liquid,md">
{% assign t = "Liquid in WebC" %}
## {{ t }}
_{{ frontmatterdata }}_
</template>
- You have full access to the data cascade here (note
frontmatterdata
is set in front matter above). - Added in @11ty/webc@0.5.0Content returned from custom transforms on
<template>
(orwebc:is="template"
) nodes will be processed as WebCβreturn any WebC content here!
Using JavaScript to Generate Content
You can also transform individual element content using webc:type
. In addition to webc:type="11ty"
, there are three more bundled types:
webc:type="js"
Added in @11ty/webc@0.7.1webc:type="render"
(superseded bywebc:type="js"
)webc:type="css:scoped"
(internal forwebc:scoped
βbut overridable!)
JavaScript Render Functions: webc:type="js"
and webc:type="render"
Added 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).
<img
src="my-image.jpeg"
alt="An excited Zach is trying to finish this documentation"
/>
<script webc:type="js" webc:root>
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:
<style webc:is="add-banner-to-css" @license="MIT licensed">
p {
color: rebeccapurple;
}
</style>
<template webc:is="style" webc:root="override">
<script webc:type="js">
`/* ${license} */`;
</script>
<slot></slot>
</template>
Expand to see this example with webc:type="render"
<template webc:is="style" webc:root="override">
<script webc:type="render">
function() {
return `/* ${this.license} */`;
}
</script>
<slot></slot>
</template>
Expand to see another example of a more complex conditional using webc:type="js"
Note that 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>
Bonus tips:
- You can use
webc:scoped webc:is="style" webc:type="js"
(orwebc:type="render"
) to generate scoped CSS using JavaScript! Read more atwebc:scoped
. - You have access to the component attributes and props in the render function (which is covered in another section!).
- Added in @11ty/webc@0.9.0Using
webc:type="js"
has an impliedwebc:is="template"
to return content that will be reprocessed as WebC (HTML). You can override this with your ownwebc:is
attribute to generate a different tag (e.g.webc:is="script"
orwebc:is="style"
). - Added in @11ty/webc@0.9.0Using
webc:type="js"
has an impliedwebc:nokeep
to skip outputting the outer node. You can addwebc:keep
to override this behavior.
Extra data for JavaScript Render Functions
webc.attributes
: Added in @11ty/webc@0.9.0 an object literal representing the current elementβs attributes.webc.renderAttributes
: Added in @11ty/webc@0.9.0 a method to render public attributes to a string.webc.filterPublicAttributes
: Added in @11ty/webc@0.10.1 a method to filterwebc.attributes
, returning an object with only public attributes. Usage:webc.filterPublicAttributes(webc.attributes)
webc.escapeText
: Added in @11ty/webc@0.10.1 encodes all characters that have to be escaped in HTML text (via theentities
package)webc.escapeAttribute
: Added in @11ty/webc@0.10.1 encodes all characters that have to be escaped in HTML attributes (via theentities
package)
Read more at Issue #104.
Expand to see an img
component example
One might imagine an <img>
component definition that merges and re-uses all host component attributes correctly like this:
<script webc:type="js" webc:root="override">
`<img ${webc.renderAttributes(webc.attributes)}>`;
</script>
webc:raw
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>
!
<template webc:raw>
Leave me out of this.
<style>
p {
color: rebeccapurple;
}
</style>
</template>
- Related:
@raw
property
webc:ignore
Added in @11ty/webc@0.9.0Use webc:ignore
to completely ignore a node and not process or output anything to do with it. Useful for server-side comments or documentation on a component.
<template webc:ignore>
Hereβs how you might use this component:
<my-component>Nothing in here will be processed</my-component>
</template>
Server-only comments
Added in @11ty/webc@0.10.0
Instead of an HTML comment that will show up in rendered output, you can add one or more dashes to the beginning/end to tell WebC to strip this from the output. Great for server-side comments.
<!--- WebC will remove this --->
<!-- This will *not* be removed and is rendered to the output -->
<!------- WebC will remove this, too ------->
Custom Transforms
This plugin provides a few transforms out of the box: webc:type="js"
, webc:type="render"
, webc:type="css:scoped"
, and webc:type="11ty"
.
However, adding your own webc:type
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 which would be available via webc:type="11ty"
(e.g. <style webc:type="11ty" 11ty:type="sass">
).
Helper Functions
WebC Helpers are JavaScript functions available in dynamic attributes, @html
, @raw
, and render functions.
Eleventy-provided Helpers
Added 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
export default 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!
};
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
Void elements
Custom elements (per specification) are not supported as void elements: they require both a starting and ending tag.
Practically speaking, this means a WebC component cannot be self-closing. You can workaround this limitation using webc:is
(e.g. <img webc:is="my-component">
).
<head>
Components
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 but
<meta webc:is="my-custom-head> is -->
<meta webc:is="my-custom-head" />
<title webc:is="my-custom-title">Default Title</title>
</head>
<table>
Components
Due to WebC's use of the parse5 library, all WebC files to be processed undergo parsing and tokenization in the same way a web browser would parse them. For this reason, putting a <table>
tag in a custom WebC element and it's <tr>
and <td>
tags in a slot to be inserted into the table will cause the <tr>
and <td>
elements to be removed upon initial parsing and all internals of the table to be placed as a sibling to itself. This is caused by the parse5 library believing the <tr>
and <td>
tags are orphaned.
To workaround this limitation, use webc:is
for the <table>
, <tr>
, and <td>
elements.
Expand for an example workaround
The above example assumes the existence of _includes/my-layout.webc
(an Eleventy layout).
...
<my-table>
<x webc:is="tr">
<x webc:is="td">
My Table Content
</x>
</x>
</my-table>
...
Rendering Modes
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.
page
is used when the markup starts with<!doctype
(or<!DOCTYPE
) or<html
(WebC forces no-quirks parsing).component
is used otherwise.
Differences from HTML parsing
Added in @11ty/webc@0.9.0WebC processes content inside of both <template>
and <noscript>
tags. The HTML parser treats these as plaintext.
Eleventy + WebC Features
Front Matter
WebC in Eleventy works automatically with standard Eleventy conventions for front matter (though front matter in Eleventy is optional).
---
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).
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>WebC Example</title>
</head>
<body @raw="content"></body>
</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
Components are the
magic of WebC and there are a few ways to define components in WebC:- Use global no-import components specified in your config file.
- Specify a glob of no-import components at a directory or template level in the data cascade.
- You can use
webc:import
inside of your components to import another component directly.
Global no-import Components
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.
We accept:
- String (file path or glob)
- Array (of file paths or globs) Added in @11ty/eleventy-plugin-webc@0.9.2
npm:
prefix aliases Added in @11ty/eleventy-plugin-webc@0.9.2
import pluginWebc from "@11ty/eleventy-plugin-webc";
export default function (eleventyConfig) {
eleventyConfig.addPlugin(pluginWebc, {
// Glob to find no-import global components
// This path is relative to the project-root!
// The default value is shown:
components: "_components/**/*.webc",
// or an Array (Eleventy WebC v0.9.2+)
components: [
"_components/**/*.webc",
"npm:@11ty/is-land/*.webc",
"npm:@11ty/eleventy-plugin-syntaxhighlight/*.webc",
],
});
};
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 value is shown:
components: "_components/**/*.webc",
// or an Array (Eleventy WebC v0.9.2+)
components: [
"_components/**/*.webc",
"npm:@11ty/is-land/*.webc",
"npm:@11ty/eleventy-plugin-syntaxhighlight/*.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. _components/my-component.webc
will give you access to <my-component>
).
Declaring Components in Front Matter
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
:
---
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:
- Use the global no-import components option.
- Use
~/
as a prefix (e.g.~/my-directory/webc/*.webc
) to alias to the projectβs root directory.
Official WebC Components
The following plugins offer official WebC components for use in your projects:
@11ty/is-land
supplies<is-land>
- Example:
<is-land webc:import="npm:@11ty/is-land">
- Read more at Use with
is-land
- Example:
@11ty/eleventy-plugin-syntaxhighlight
supplies<syntax-highlight>
- Example:
<syntax-highlight language="js" webc:import="npm:@11ty/eleventy-plugin-syntaxhighlight">
- Read more at Syntax Highlighting Plugin
- Example:
@11ty/eleventy-img
supplies<eleventy-image>
- Added in Image v3.1.0
- Example:
<img webc:is="eleventy-image" webc:import="npm:@11ty/eleventy-img">
- Read more at the Image utility.
CSS and JS (Bundler mode)
Eleventy WebC will 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>
, <script src>
, <style>
, or <link rel="stylesheet">
. You can use this to implement component-driven Critical CSS.
<template shadowrootmode>
or the deprecated <template shadowroot>
) are left as is and not bundled.<style>
/* This is component CSS */
</style>
<script>
/* This is component JS */
</script>
<!-- Local 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
.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>WebC Example</title>
<!-- inline bundles -->
<style @raw="getBundle('css')" webc:keep></style>
<script @raw="getBundle('js')" webc:keep></script>
<!-- or write your bundle to a file -->
<link rel="stylesheet" :href="getBundleFileUrl('css')" webc:keep />
<script :src="getBundleFileUrl('js')" webc:keep></script>
</head>
<body @raw="content"></body>
</html>
- Added in @11ty/eleventy-plugin-webc@0.9.0Eleventy WebC uses the Bundle Plugin behind the scenes to implement bundling.
getBundle('css')
andgetBundle('js')
can now be used instead ofgetCss(page.url)
andgetJs(page.url)
respectively. - Added in @11ty/webc@0.8.0
webc:keep
is required on<style>
and<script>
in your layout files to prevent re-bundling the bundles. - Added in @11ty/webc@0.8.0The
getCss
andgetJs
helpers are now available to all WebC templates without restriction. Previous versions required them to be used in an Eleventy Layout file. @raw
was Added in @11ty/webc@0.7.1. Previous versions can usewebc:raw @html
.
Bundle Code Ordering
The order of the code in these bundles is determined by the dependency order of the components, from most specific to least specific!
Expand to see an example
Say we have an index.webc
page that uses a header.webc
component.
<style>
/* index.webc */
</style>
<header></header>
<style>
/* header.webc */
</style>
The CSS bundle will look like:
/* header.webc */
/* index.webc */
Access Bundles in other Template Engines
You can access these bundles in other templates types too (.njk
, .liquid
, etc.).
Added in @11ty/eleventy-plugin-webc@0.9.0Eleventy WebC uses the Bundle Plugin behind the scenes to implement bundling. This plugin provides getBundle
and getBundleFileUrl
universal shortcodes for use in any template type (including WebC as shown above).
WebC v0.8.0 and older: Check out the deprecated (but still in place for backwards compatibility) webcGetCss
and webcGetJs
universal filters for bundle output.
<style>{{ page.url | webcGetCss | safe }}</style>
<script>{{ page.url | webcGetJs | safe }}</script>
<!-- write to a file -->
<link rel="stylesheet" href="{% getBundleFileUrl "css" %}">
<style>{{ page.url | webcGetCss }}</style>
<script>{{ page.url | webcGetJs }}</script>
Asset bucketing
There is an additional layer of bundling here that you can use that we call Bucketing. Components can use webc:bucket
to output to any arbitrary bucket name.
In this component, we have component code that outputs to two separate buckets:
<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>
When <my-webc-component>
is used on a page, it will roll the assets to the page-specific bucket bundles for CSS and JavaScript.
Then you can output those bucket bundles anywhere on your page like this (here weβre using an Eleventy layout file):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>WebC Example</title>
<!-- Default bucket -->
<style @raw="getBundle('css')" webc:keep></style>
<script @raw="getBundle('js')" webc:keep></script>
</head>
<body>
<template @raw="content" webc:nokeep></template>
<!-- `defer` bucket -->
<style @raw="getBundle('css', 'defer')" webc:keep></style>
<script @raw="getBundle('js', 'defer')" webc:keep></script>
</body>
</html>
- Added in @11ty/webc@0.8.0
webc:keep
is required on<style>
and<script>
in your layout files to prevent re-bundling the bundles. - Added in @11ty/webc@0.9.1
:webc:bucket
(dynamic attribute) is supported to set this value via JavaScript. #120
Cascading Asset Buckets
Added in @11ty/webc@0.9.1 Additionally webc:bucket
can be added to any tag and will cascade to all child content.
Consider this WebC page:
<!-- has an implied webc:bucket="default" -->
<my-component></my-component>
<div webc:bucket="defer">
<!-- each of these have webc:bucket="defer" -->
<!-- (including any nested components inside, too) -->
<footnote-references></footnote-references>
<my-footer></my-footer>
</div>
Setting webc:bucket
now cascades to all of the children as if they had webc:bucket="defer"
assigned to each of them individually. All assets used in those components will now be rolled up into the defer
bucket.
Conflicts and hoisting
What happens when a component is used in multiple distinct buckets?
<!-- has an implied webc:bucket="default" -->
<my-component></my-component>
<div webc:bucket="defer">
<my-component></my-component>
</div>
When duplicates and conflicts occur, WebC will hoist the component code to find the nearest shared bucket for you. In the above example, the CSS and JS for <my-component>
will be loaded in the default
bucket and only in the default
bucket.
Use with is-land
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.
<is-land on:visible webc:import="npm:@11ty/is-land">
<template data-island>
<!-- CSS -->
<style webc:keep>
/* This CSS applies on:visible */
</style>
<link rel="stylesheet" href="arbitrary.css" webc:keep />
<!-- JS -->
<script type="module" webc:keep>
console.log("This JavaScript runs on:visible");
</script>
<script type="module" src="arbitrary.js" webc:keep></script>
</template>
</is-land>
From the Community
Γ47 resources courtesy of 11tybundle.dev curated by Bob Monsour