DOCS
v0.4

@cognivo/ssr

Server-side rendering for Cognivo Lit components. A thin wrapper over @lit-labs/ssr that emits declarative shadow DOM inline — eliminating FOUC in Next.js, Astro, and Remix.

Installation

pnpm add @cognivo/ssr @cognivo/components

Peer deps (lit, @lit-labs/ssr) ship as direct dependencies of the package — you do not need to install them explicitly. Requires Node 20+.

Quick start

import { renderToString, html } from '@cognivo/ssr';
import '@cognivo/components';

const rendered = renderToString(html`
  <cg-card>
    <cg-button variant="primary">Hello SSR</cg-button>
  </cg-card>
`);

// rendered contains <template shadowrootmode="open">…</template> blocks
// that modern browsers attach automatically on parse.

API Reference

ExportSignatureDescription
renderToString (t: TemplateResult) => string Sync render to HTML. Throws if the template contains async directives.
renderToStringAsync (t: TemplateResult) => Promise<string> Async variant — supports Promises, until, streaming children.
renderToStream (t: TemplateResult) => ReadableStream<string> Stream HTML chunks. Pair with new Response(stream) for progressive delivery.
html tagged template Re-exported from lit so consumers do not need a direct Lit dependency.
TemplateResult type Re-exported Lit template type.

Subpath entries @cognivo/ssr/next and @cognivo/ssr/astro re-export the same helpers for convenience and discoverability.

Framework Examples

Next.js App Router

// app/page.tsx — React Server Component
import { renderToString, html } from '@cognivo/ssr/next';
import '@cognivo/components';

export default function Page() {
  const rendered = renderToString(html`
    <cg-card>
      <cg-button variant="primary">Hello from RSC</cg-button>
    </cg-card>
  `);
  return <div dangerouslySetInnerHTML={{ __html: rendered }} />;
}

Import @cognivo/components once from a 'use client' root layout so the custom elements register on the client and hydrate the declarative shadow roots.

Astro

---
import { renderToString, html } from '@cognivo/ssr/astro';
import '@cognivo/components';

const rendered = renderToString(html`
  <cg-card><cg-button>Hello Astro</cg-button></cg-card>
`);
---
<Fragment set:html={rendered} />
<script>import '@cognivo/components';</script>

Remix — streaming loader

// app/routes/_index.tsx
import { renderToStream, html } from '@cognivo/ssr';
import '@cognivo/components';

export async function loader() {
  const stream = renderToStream(html`<cg-dashboard></cg-dashboard>`);
  return new Response(stream, {
    headers: { 'Content-Type': 'text/html' },
  });
}

Caveats

  • Declarative shadow DOM is supported natively in Chromium 111+, Safari 16.4+, and Firefox 123+. Older browsers render the flat HTML but skip shadow boundaries — styles will leak until the client bundle defines the custom elements.
  • For legacy browser support, ship the @webcomponents/template-shadowroot polyfill before registering components on the client.
  • renderToString throws on async directives (until, Promises in templates). Use renderToStringAsync or renderToStream in those cases.
  • You still need to import @cognivo/components on the client for interactivity — SSR gives you styled markup, not event handlers.