Logo

bausteine

Documentation

Server-Side Rendering

Bausteine can be rendered on the server with hydro-js-integrations. The server creates a DOM, renders your hydro-js templates into it, and serializes the document. The browser then loads your normal client bundle and upgrades the same custom elements.

npm i hydro-js-integrations

Minimal Deno + Hono server

Import your SSR setup once, stream the serialized document for application routes, and still serve static build files for assets and client JavaScript.

import type { HtmlEscapedString } from "hono/utils/html";
import { Hono } from "hono";
import { serveStatic } from "hono/deno";
import { renderToReadableStream } from "hono/jsx/streaming";
import { renderRootToString } from "hydro-js-integrations/server";
import "./ssr.ts";

const app = new Hono();

app.get("/", (c) => {
  const stream = renderToReadableStream(
    ("<!doctype html>" + renderRootToString()) as HtmlEscapedString,
  );

  return c.body(stream, {
    headers: {
      "Content-Type": "text/html; charset=UTF-8",
      "Transfer-Encoding": "chunked",
    },
  });
});

app.use("*", serveStatic({ root: "./build" }));

Deno.serve({ port: 3000 }, app.fetch);

Render Bausteine components

Call getLibrary() before importing Bausteine component modules. This initializes the server DOM first, which is important because createWebComponent reads window when components are registered.

import { getLibrary } from "hydro-js-integrations/server";

const { html, render } = await getLibrary();
const { default: Button } = await import("./components/Button/index.ts");
const { default: Checkbox } = await import("./components/Checkbox/index.ts");

Button();
Checkbox();

render(
  html`<link rel="stylesheet" href="/index.css" />
    <script type="module" src="/exampleInit.js"></script>`,
  document.head,
  false,
);

render(
  html`<main class="grid gap-4">
    <button is="bau-button" class="inline-block border p-4">
      Server-rendered button
    </button>
    <bau-checkbox checked>
      <span class="pl-4">Server-rendered checkbox</span>
    </bau-checkbox>
  </main>`,
  document.body,
  false,
);

Client entry stays the same

Keep the regular browser entry in the serialized page. It registers the same elements and lets Bausteine reuse the light DOM. During SSR, default and named slots are marked with data-default-slot and data-slot so the client render can connect to existing content instead of starting from an empty custom element.

import Button from "./components/Button/index.ts";
import Checkbox from "./components/Checkbox/index.ts";

Button();
Checkbox();

For Vite, Astro, Next.js, and Fresh setup, see the full hydro-js-integrations guide.