Joy DOM

Publishing

Ship the Joy DOM document in a real app.

The card is finished. This chapter is about getting it in front of users - where to keep the JSON, how to load it at runtime, and how to make sure print and offline both work.

Where the document lives

There are three common patterns. Pick whichever fits your project shape.

Bundled with the app

For documents that ship as part of your build, import the JSON directly. Bundlers handle the parsing:

import  from "./content/business-card.json";
import {  } from "@joy-dom/react";
import type { Spec } from "@joy-dom/spec";

export function () {
  return < ={ as Spec} />;
}

Use this for static documents, examples, marketing pages.

Fetched at runtime

For documents that change without redeploying the app, fetch the JSON from an API or static file:

import { ,  } from "react";
import {  } from "@joy-dom/react";
import { , type Spec } from "@joy-dom/spec";

export function ({  }: { : string }) {
  const [, ] = <Spec | null>(null);

  (() => {
    ()
      .(() => .())
      .(() => .())
      .();
  }, []);

  if (!) return null;
  return < ={} />;
}

The SpecSchema.parse call hardens the boundary - if the API ever serves something malformed, you see a clear error instead of a downstream rendering crash.

Generated by a server

For per-user documents (an invoice, a receipt, a report), generate the JSON on the server and send it down with the page. Same shape as fetched, just with the document inlined in the initial HTML or hydrated state.

Validation in CI

SpecSchema is a Zod schema, so you can run it anywhere. Add a CI step that validates every JSON document in the repo:

import {  } from "node:fs";
import {  } from "node:fs/promises";
import {  } from "@joy-dom/spec";

for await (const  of ("content/**/*.json")) {
  const  = .((, "utf8"));
  const  = .();
  if (!.) {
    .(`${} is invalid:\n${.}`);
    process.exit(1);
  }
}

Run it in CI. Documents stay valid forever.

Print

The print media type works at the renderer level. Trigger print preview from the browser (⌘P or Ctrl+P) and any breakpoint with { "type": "type", "value": "print" } activates. See chapter 3 for the print breakpoint we added to the card.

PDF export

Browsers let users save print preview as PDF. That's the simplest "Joy DOM → PDF" path today. Server-side rendering to PDF (Puppeteer, Playwright) works too - point a headless browser at the rendered page and use page.pdf().

Other renderers

The same JSON renders in Swift and Kotlin apps. See:

The document is portable; only the integration code per platform changes.

Where next

You've now seen the whole loop: build a document, validate it, style it, make it responsive, extend it with custom components, and ship it.

For deeper reference:

  • Specification - every supported property, condition, and rule, with stable section numbers.
  • Recipes - real-world documents you can clone and adapt.
  • Renderers - install, config, and API per renderer.

On this page