Litro's adapter system lets you choose which web component framework powers your project. Every adapter shares the same infrastructure — file-based routing, definePageData(), LitroRouter, content layer, SSG, deployment adapters — only the component authoring model differs.
Two adapters are supported: lit and fast. A third, elena, is
deprecated and removed in Litro v1 — see
the Elena adapter for why and what to do about it. It
still works, and it is still on the pages below, because projects that already
use it must keep building until v1. It is not a choice for a new project.
| Adapter | Framework | DOM Model | SSR Strategy | Hydration | CSS Scoping |
|---|---|---|---|---|---|
| lit (default) | Lit 3 | Shadow DOM | @lit-labs/ssr (DSD) | Yes | Shadow DOM |
| fast | FAST Element 2 | Shadow DOM | @microsoft/fast-ssr (DSD) | Yes | Shadow DOM |
| elena (deprecated, removed in v1) | Elena | Light DOM | Direct rendering | No (progressive enhancement) | @scope CSS |
Use Lit if you want the most mature web component library with the largest ecosystem, decorator-based APIs, and first-class TypeScript support. This is the default and most battle-tested option.
Use FAST Element if you prefer Microsoft's observable-based reactivity model, auto-generated attributes, or need compatibility with Fluent UI Web Components.
Do not start a new project on Elena. It is deprecated and removed in v1. Its light DOM rendering is genuinely the smallest payload of the three, but ui() throws on it, so no agent tool can return a component — and that is the feature Litro leads with. If light DOM with no hydration is what you need, say so in an issue rather than building on a surface with an end date.
The --adapter flag selects the framework at project creation:
# Lit (default — can omit the flag)
pnpm create @beatzball/litro my-app --adapter lit
# FAST Element
pnpm create @beatzball/litro my-app --adapter fast
# Elena — deprecated, removed in v1, prints a notice
pnpm create @beatzball/litro my-app --adapter elena
Or omit --adapter and choose interactively during scaffolding. The prompt
offers lit and fast; Elena has to be named on the command line.
Each recipe declares which adapters it can produce, and asking for one it cannot fails before anything is written:
| Recipe | Adapters |
|---|---|
fullstack | lit, elena |
11ty-blog | lit |
starlight | lit, fast, elena |
supernova | lit |
The interactive prompt offers only the adapters the chosen recipe supports, minus the deprecated ones. --adapter still resolves every adapter in the table above, so a recipe that lists elena keeps producing it.
Regardless of which adapter you choose, these features work identically:
pages/ directory convention, dynamic segments, catch-all routesdefinePageData() — server-side data fetching, serialized into the HTML shellLitroRouter — client-side SPA navigation via <litro-link> and history.pushStatelitro:content virtual module for Markdownserver/api/ with H3 handlersgenerateRoutes() for static prerenderingEach adapter provides its own native implementations of three internal components:
| Component | Purpose |
|---|---|
| LitroOutlet | Mounts the router, serves as the container for route content |
| LitroLink | Intercepts clicks for SPA navigation, falls back to <a> |
| LitroPage | Reads __litro_data__ before first render, exposes serverData |
These are imported from adapter-specific paths — see each adapter's guide for details.
All three adapters SSR page content, but the completeness of the static HTML differs:
| Feature | Lit | FAST | Elena |
|---|---|---|---|
| Template bindings | Full | Full | Full |
| Conditional templates | Full | Full | Full |
| List rendering | Full | Full (repeat()) | Full |
| Raw HTML injection | Full (unsafeHTML) | Client-only (:innerHTML) | Full (unsafeHTML) |
| Nested component expansion | Full (DSD) | Full (DSD) | Full (recursive) |
| crawlLinks discovery | Full | Partial (misses :innerHTML links) | Full |
| Lighthouse measurability | SSG only | SSG only | SSG + SSR |
The main difference: FAST's :innerHTML property binding does not emit content during SSR, so raw HTML (comment trees, Markdown content) only appears after client-side hydration. This also means crawlLinks may miss <a> tags inside :innerHTML content. Use explicit prerender.routes when :innerHTML contains links.
Elena produces the smallest static HTML because it uses light DOM (no <template shadowrootmode> wrappers), while Lit produces the largest because DSD duplicates styles per shadow root instance. All three render full content for search engines in SSG mode, with the caveat above for FAST's :innerHTML.
Where an adapter limitation requires a workaround, it currently lives in the app, not the framework — users need to know what they'll own.
:innerHTML fill — the HN benchmark app fills :innerHTML content via a post-build Nitro compiled hook that reads the serialized __litro_data__ and replaces empty markers in the static HTML. This keeps the benchmark comparison fair, but the underlying adapter limitation still applies to any FAST project — teams must add their own post-build step (or avoid :innerHTML for SSG-critical content) until a framework-level fix lands.unsafeHTML() is the standard public API for raw HTML (not a workaround); Next.js's output: 'export' and Nuxt's prerender.routes are first-class static export config.Scope caveat: the HN benchmark is intentionally narrow — list pages, param routes, nested comments, no forms, no streaming SSR, no user input. "No workarounds needed" is a claim about this scope, not a proof of full adapter parity under every real-world workload.
Lighthouse note: Lighthouse measures Declarative Shadow DOM correctly when scoring static (SSG) output, so Lit, FAST, and Elena all return real performance scores in the HN benchmark numbers. We deliberately skip per-request SSR Lighthouse runs because the scored variable in that mode is server warm-up cost rather than the framework's render path, which makes adapter-to-adapter comparison noisy. None of the adapters score 0 because of Shadow DOM — the content is visible to users and to the scoring engine alike.
All three adapters are benchmarked with identical Hacker News clones (~100 pages each) alongside Next.js and Nuxt. Current benchmarks cover static prerendering (SSG) — build times, output sizes, and page weight comparisons. Per-request SSR benchmarks (TTFB, throughput, latency) are planned for a future update. See the benchmarks page for results.