Litro vs Enhance
Enhance (by Begin.dev) and Litro are the two main frameworks for server-rendering web components without React or Vue. They share the same fundamental goal and differ significantly in how they get there.
What They Share
- Server-first web components — HTML is rendered on the server before any JavaScript runs
- No React, Vue, or Svelte in the component model
- Progressive enhancement — pages work without JavaScript
- TypeScript support
- Standard Custom Elements at runtime
Feature Comparison
| Feature | Enhance | Litro |
|---|---|---|
| Component format | HTML-first SFCs | Lit class components |
| Server rendering | ✓ | ✓ Declarative Shadow DOM |
| Client-side routing | — (MPA by default) | ✓ LitroRouter |
| Server engine | Begin cloud / Arc | Nitro (Vercel, Cloudflare, AWS…) |
| Deployment flexibility | Begin-centric | All Nitro presets |
| JavaScript by default | Opt-in | Opt-in |
| Lit ecosystem (Shoelace, etc.) | — | ✓ |
| TypeScript | Partial | ✓ Full |
| SSG | ✓ | ✓ |
| File-based routing | ✓ | ✓ |
Component Authoring
Enhance uses its own HTML-first Single File Component format. Components are pure
functions that return HTML strings, with optional client-side enhancement via
<script> tags. This is intentionally minimal — no library layer.
Litro uses Lit — a 6 kB library that adds reactive properties, tagged template rendering, and lifecycle callbacks on top of the native Custom Elements API. Lit is widely adopted (used by Google, Adobe, and others) with its own ecosystem of components, tools, and community.
Enhance — app/elements/my-counter.mjs
export default function MyCounter({
html,
state: { attrs }
}) {
const count = attrs.count ?? 0;
return html`
<style>
button { padding: 0.5rem 1rem; }
</style>
<button>Count: ${count}</button>
<script type="module">
// client enhancement here
</script>
`;
}
Litro — Lit component
import { html, css, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
@customElement('my-counter')
class Counter extends LitElement {
static styles = css`
button { padding: 0.5rem 1rem; }
`;
static override properties = {
_count: { state: true },
};
_count = 0;
render() {
return html`
<button @click=${() => this._count++}>
Count: ${this._count}
</button>
`;
}
}
Server and Deployment
Enhance is built for Begin's cloud platform and Arc (the AWS infrastructure-as-code tool Begin maintains). It works on AWS Lambda natively and has first-party Begin support. Other deployment targets require more manual configuration.
Litro uses Nitro, which has first-party deployment presets for Vercel, Cloudflare Workers, AWS Lambda, Deno Deploy, Netlify, Bun, and more. These are the same presets used by Nuxt.js in production across thousands of sites.
Client-Side Routing
Enhance is primarily a multi-page application (MPA) framework. Navigation triggers full
page loads by default. Client-side interactivity is added progressively via <script>
tags in component files. There's no built-in SPA router.
Litro includes LitroRouter — a built-in client-side router built on the
native URLPattern API. Navigation between routes is SPA-style with no full page reload.
Use <litro-link> for SPA navigation; plain <a> tags
do full page reloads.
When Enhance Fits Well
Enhance is worth considering if you want:
- Zero JavaScript by default — pages are static HTML, enhancement is strictly opt-in
- The simplest possible component authoring model (no library dependency)
- A Begin/Arc-centric deployment model (deep AWS integration out of the box)
- Minimal abstraction — components are functions, not classes
When to Choose Litro
Litro is a better fit if you want:
- Client-side routing (SPA-style navigation without full page reloads)
- The Lit ecosystem — Shoelace,
@lit/context, community components - Nitro's deployment flexibility (Vercel, Cloudflare Workers, and more)
- Migrating from Nuxt.js — the server layer is identical
- Full TypeScript across the component model
Litro