π The Future of Server-Rendered Web Components: Enhance vs. Lit vs. WebC π
Which is Best for Server-Rendered Web Components: Enhance, Lit, or WebC?

Web components have been around for a while, but server-side rendering (SSR) of custom elements has always been a tricky subject. With modern frameworks moving towards server-first architectures, the demand for HTML-first, SSR-friendly components has never been higher.
So today, let's break down three popular ways to server-render web components in Node.js using:
β Enhance
β Lit
β WebC
We'll explore how they work, their differences, and which one is best suited for building scalable, high-performance applications.
π‘ The Big Question: Can You Server-Render Web Components?
Absolutely! Web components have always been renderable on the server. Even without JavaScript, you can output basic custom elements on any backend.
Here's a simple example of an SSR-friendly custom element:
<my-custom-element>
<my-custom-header><h1>Welcome!</h1></my-custom-header>
<my-custom-button variant="primary"><button>Click Me</button></my-custom-button>
</my-custom-element>
And the CSS:
my-custom-element {
display: block;
background: lightyellow;
padding: 1rem;
}
my-custom-header {
display: block;
padding-bottom: 1rem;
border-bottom: 1px solid rebeccapurple;
}
my-custom-button[variant="primary"] button {
background: navy;
color: white;
}
π₯ No JavaScript needed. The browser renders it just fine. Butβ¦ is that enough?
In modern component-driven applications, we need:
β
Shadow DOM support
β
Declarative server rendering
β
Hydration for interactivity
This is where Enhance, Lit, and WebC come in!
β‘ Enhance: The Lightweight Server-First Framework
Enhance is a fullstack framework designed with SSR web components in mind. The best part? Itβs a standalone library, meaning you can use it in any Node.js app.
π Example Enhance Component
export default function MyElement({ html, state }) {
const { attrs } = state;
const { hello = "?", easy = [] } = attrs;
return html`
<style>
:host {
display: block;
padding: 0.5rem 1rem;
border: 3px double orange;
}
</style>
<h2>Hello ${hello}!</h2>
<p>Easy as ${easy.join(", ")}. <slot></slot></p>
`;
}
And the server-side rendering code:
import MyElement from "./my-element.js";
import enhance from "@enhance/ssr";
const html = enhance({
elements: { 'my-element': MyElement },
});
const data = { hello: "world", easy: [1, 2, 3] };
const result = html`
<my-element hello=${data.hello} easy=${data.easy}>Hello from SSR!</my-element>
`;
console.log(result);
π Why Use Enhance?
β
Simple & lightweight
β
No Shadow DOM by default (great for flexibility)
β
0kb JavaScript philosophy
Enhance is great if you want pure SSR components with easy expansion. However, it lacks built-in isomorphic rendering (components running both on server and client).
π₯ Lit: The Isomorphic Powerhouse
Lit is a popular web component library that provides both client-side and server-side rendering. Unlike Enhance, Lit uses Shadow DOM by default and works well in isomorphic setups.
π Example Lit Component
import { LitElement, html, css } from "lit";
export class MyElement extends LitElement {
static styles = css`
:host {
display: block;
padding: 0.5rem 1rem;
border: 3px double orange;
}
`;
static properties = {
hello: {},
easy: { type: Array },
};
constructor() {
super();
this.hello = "?";
this.easy = [];
}
render() {
return html`
<h2>Hello ${this.hello}!</h2>
<p>Easy as ${this.easy.join(", ")}. <slot></slot></p>
`;
}
}
customElements.define("my-element", MyElement);
π How to Server-Render Lit Components
import { render } from "@lit-labs/ssr";
import { collectResult } from "@lit-labs/ssr/lib/render-result.js";
import { html } from "lit";
import { MyElement } from "./my-element.js";
const result = await collectResult(render(html`
<my-element hello="world" easy=${[1, 2, 3]}>Hello from SSR!</my-element>
`));
console.log(result);
π Why Use Lit?
β
Shadow DOM for encapsulation
β
Great for isomorphic apps (client & server run the same code)
β
Backed by Google & has a strong ecosystem
However, Litβs SSR mechanism is still evolving, and using it requires some workarounds.
π WebC: The Future of HTML-First SSR
WebC (Web Components Compiler) is a unique, HTML-based approach to writing server-rendered web components. It was created by Eleventyβs team and is quickly gaining traction.
π Example WebC Component
<script webc:setup>
const join = (input) => input.join(", ");
</script>
<style webc:scoped="my-element">
:host {
display: block;
padding: 0.5rem 1rem;
border: 3px double orange;
}
</style>
<h2>Hello <span @text="hello"></span>!</h2>
<p>Easy as <span @text="join(easy)">...</span>. <slot></slot></p>
π₯ How to Server-Render WebC Components
import { WebC } from "@11ty/webc";
const page = new WebC();
page.defineComponents("./components/*.webc");
page.setInputPath("./page.webc");
page.setBundlerMode(true);
const { html } = await page.compile({
data: { hello: "world", easy: [1, 2, 3] },
});
console.log(html);
π Why Use WebC?
β
True HTML-first approach
β
No JavaScript required for basic components
β
Built for SSR-first workflows
WebC is perfect for Eleventy and other SSR-friendly environments, but it lacks built-in client-side hydration.
π Which One Should You Use?
| Feature | Enhance | Lit | WebC |
| Shadow DOM | β No | β Yes | β Optional |
| SSR Support | β Yes | β Yes | β Yes |
| Client Hydration | β Yes | β Yes | β Limited |
| HTML-First | β Yes | β No | β Yes |
My Pick? If you love HTML-first development, WebC is the future. However, if you need isomorphic components, Lit might be a better fit. And for pure SSR setups, Enhance is a solid choice!
What do you think? Would you switch to SSR web components? Let me know in the comments! π¬π
Hope you like this breakdown! Stay tuned for more web dev insights. πβ¨



