@elenajs/ssr
v1.0.0-alpha.10
Published
Render Elena components to HTML strings for server-side rendering.
Readme
Render Elena components to HTML strings for server-side rendering
[!WARNING] Please note that
@elenajs/ssris an experimental package and not yet ready for production use. APIs may change without notice.
Table of contents
Install
npm install @elenajs/ssrUsage
Basic example
import { ssr, register } from "@elenajs/ssr";
const { Button } = await import("@elenajs/components");
register(Button);
const html = ssr(`<elena-button variant="primary">Save</elena-button>`);
// Outputs: '<elena-button variant="primary"><button>Save</button></elena-button>'With nesting
Nested Elena components are expanded automatically:
import { ssr, register } from "@elenajs/ssr";
const { Button, Stack, Input } = await import("@elenajs/components");
register(Button, Stack, Input);
const html = ssr(`
<elena-stack direction="row">
<elena-input label="Email" type="email" placeholder="[email protected]"></elena-input>
<elena-button>Send</elena-button>
</elena-stack>
`);Output:
<elena-stack direction="row">
<elena-input type="email" placeholder="[email protected]">
<label for="email">Email</label>
<input id="email" type="email" placeholder="[email protected]" />
</elena-input>
<elena-button><button>Send</button></elena-button>
</elena-stack>With a full page
import { ssr, register } from "@elenajs/ssr";
const { Button } = await import("@elenajs/components");
register(Button);
const page = ssr(`
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="/components/button.css">
</head>
<body>
<elena-button variant="primary">Click me</elena-button>
<script type="module" src="/components/button.js"></script>
</body>
</html>
`);With Eleventy
You can use @elenajs/ssr with Eleventy as either a transform or a shortcode.
As a transform
A transform processes every rendered page automatically, expanding any registered components found in the output HTML. No shortcodes or special syntax needed, just write Elena components directly in your templates:
// eleventy.config.js
import { ssr, register } from "@elenajs/ssr";
const { Button, Stack } = await import("@elenajs/components");
register(Button, Stack);
export default function (eleventyConfig) {
eleventyConfig.addTransform("elena-ssr", (content, outputPath) => {
if (outputPath?.endsWith(".html")) {
return ssr(content);
} else {
return content;
}
});
}Note: Use
await import()for component modules rather than a staticimportstatement. Elena components extendHTMLElement, which requires a Node.js shim that@elenajs/ssrinstalls when it loads. Dynamic imports guarantee the shim is in place first, regardless of how an import sorter may reorder your static imports.
Then use Elena components directly in any Nunjucks, Liquid, or Markdown template:
<elena-stack direction="row">
<elena-input type="email" placeholder="[email protected]"></elena-input>
<elena-button variant="primary">Subscribe</elena-button>
</elena-stack>As a shortcode
If you prefer more control over which parts of a page are processed, use a shortcode instead:
// eleventy.config.js
import { ssr, register } from "@elenajs/ssr";
const { Button } = await import("@elenajs/components");
register(Button);
export default function (eleventyConfig) {
eleventyConfig.addShortcode("render", (html) => ssr(html));
}Then in a template:
{% render '<elena-button variant="primary">Save</elena-button>' %}API
register(...components)
Register Elena component classes for SSR expansion. Each class must have static tagName defined. Call this once before using ssr().
import { register } from "@elenajs/ssr";
const { Button, Stack } = await import("@elenajs/components");
register(Button, Stack);Throws an error if a component does not have a tagName.
unregister(...components)
Remove previously registered component classes from the SSR registry.
import { register, unregister } from "@elenajs/ssr";
const { Button } = await import("@elenajs/components");
register(Button);
// ... later
unregister(Button);clear()
Remove all registered component classes from the SSR registry at once.
import { clear } from "@elenajs/ssr";
clear();ssr(html)
Parse an HTML string, expand registered components with render(), and return the rendered HTML. Full HTML documents are supported: <!DOCTYPE>, <html>, <head>, and <body> tags are preserved as-is alongside component expansion.
| Parameter | Type | Description |
| --------- | -------- | ---------------------------------------- |
| html | string | HTML string containing Elena components. |
Returns: string, the rendered HTML with components expanded.
How it works
- Parse the input HTML string into a tree.
- Walk the tree and look up each custom element tag in the registry.
- Expand matching custom elements by calling their
render(). - Recurse into composite component children and non-component tags.
- Serialize the tree back to an HTML string.
The rendered output matches what Elena produces on the client, using the same html tagged template escaping and whitespace normalization.
If a component's render() throws an error, the SSR renderer logs a warning and falls back to passing the component through without expansion, preserving its original children.
Client-side hydration
The HTML produced by ssr() is designed for progressive enhancement. When the component JavaScript loads on the client:
- Elena's
connectedCallbackfires on the pre-rendered element. render()runs and hydrates the component with interactivity.- Event listeners are attached and methods become available.
License
MIT
Copyright
Copyright © 2026 Ariel Salminen
