Publishing & the Public Renderer
When you publish a document, BlnkSpace renders it through a server-side async React component called the Block Renderer. This page explains how publishing works end-to-end — from enabling a public URL to how each block type is rendered for visitors.
Publishing a document
Every document in BlnkSpace has a Published toggle. When turned on, the document becomes accessible at its public URL. When turned off, visitors see a 404 — the document and its content are never exposed.
How the renderer works
Published pages are rendered by an async React Server Component — no client-side JavaScript is required to display the content. The renderer receives the document's blocks, layout (navbar/footer), typography settings, and colors, then converts each block to its HTML equivalent server-side.
The rendering pipeline:
- The middleware rewrites tenant hostnames (your subdomain or custom domain) to an internal
/site-renderroute, passing the host and path as query params. - The
site-renderpage resolves the document from the host/path combination, fetches the blocks and layout, then passes them toBlockRenderer. BlockRendereris a server component. It iterates all blocks, recursively resolving column children, section children, and toggle children, rendering each into React nodes.- The final HTML is sent to the browser. Most content requires zero JavaScript to display.
ISR caching
Published pages are cached using Next.js Incremental Static Regeneration (ISR) with a 60-second revalidation window. This means:
- The first visit after a cache miss renders the page fresh from the database.
- Subsequent visits within 60 seconds are served from the CDN cache — extremely fast.
- After 60 seconds, the next request triggers a background revalidation. Visitors still see the cached version while the new one builds.
- Content changes appear on the public page within ~60 seconds of saving in the editor.
Typography on published pages
The document's typography tokens (font family, font size, line height, letter spacing) are applied to each block via a blockWrapStyle function. This function reads the document-level typography and merges it with any block-level overrides from the selection toolbar.
The cascade is:
- Block-level override (set via selection toolbar) wins first.
- Document typography (set via the Typography picker or a branding preset) applies if no block override is set and the value is non-default.
- Hardcoded fallback (the renderer's own defaults) applies last.
'normal' (for size/line height/letter spacing) and 'inter' or 'default' (for font family) are treated as "not set" by the renderer — they don't override the block's own defaults. Only non-default values cascade down.Google Fonts on published pages
If the document uses a Google Font (for the body, navbar, or footer), the font stylesheet is injected server-side into the page HTML. No JavaScript is required — the <link> tag appears in the rendered HTML so the browser starts fetching the font immediately. Fonts are deduplicated: if the body and navbar share the same font, only one request is made.
Block rendering reference
The table below shows how each block type is handled in the public renderer. "SSR" means the block renders entirely server-side with no client JavaScript. "Client island" means the block requires a client component for interactivity but falls back gracefully if JS is slow to load.
| Block type | Render mode | Notes |
|---|---|---|
| Paragraph, Headings | SSR | TipTap HTML rendered server-side. Typography tokens applied via wrapStyle. |
| List, Numbered, Todo | SSR | Bullet/number markers rendered as inline spans. Typography tokens applied. |
| Quote, Callout | SSR | Border and background from theme. Content via TipTap HTML. |
| Divider | SSR | Horizontal rule with configurable color. |
| Button | SSR | Rendered as an anchor tag with inline styles. |
| Toggle | Client | Expand/collapse requires a client island. Content children are SSR. |
| Code Block | SSR | Raw code in a <pre> tag. No syntax highlighting on published pages. |
| Image | Client | Dither and ASCII display modes require canvas — rendered as client islands. Default/clean modes are SSR. |
| Embed | Client | iframe — client only. |
| Hero Section | Client | Background effects (Three.js, shaders) require client. Static hero variants are SSR. |
| Section | SSR | Background image, overlay, and child blocks all SSR. |
| Carousel | Client | Swipe/drag requires client. SSR fallback shows first slide. |
| Tab Bar | Client | Tab switching is client-side. Content of all tabs is SSR. |
| Infinite Carousel | Client | CSS animation set via JS on mount. |
| Database | SSR | Table view rendered server-side. Row peek and editing are editor-only. |
| Spreadsheet | SSR | Rendered as a static HTML table. No editing on published pages. |
| Equation | SSR | KaTeX renders to HTML server-side. |
| Bibliography | SSR | Reference list and inline citations rendered server-side. |
| 3D Scene | Client | Three.js and Spline are dynamically imported (ssr: false). Shows loading placeholder until ready. |
| Product blocks | Client | Commerce blocks use PublicCommerceShell — one server fetch, shared across all commerce blocks on the page. |
| YouTube Shorts | Editor only | This block does not render on published pages — it's a management/analysis tool. |
| Product CMS | Editor only | Management interface only. Not rendered on published pages. |
Navbar and footer
If a document has useNavbar or useFooter enabled, the site layout's navbar/footer configuration is rendered above and below the block content respectively. Both are SSR — no client JavaScript required for the navigation structure itself. Dropdown menus require a client island for the open/close interaction.
If no footer is configured, BlnkSpace renders a minimal attribution footer with a "Made with BlnkSpace" link.
Content width
The document's width setting controls how content blocks are constrained. Named widths map to Tailwind classes; custom pixel values use inline styles. Full-width blocks (Hero, Section, Carousel, Infinite Carousel) always span 100% of the viewport regardless of the document width setting.
| Setting | Class / value |
|---|---|
| narrow | max-w-2xl (672px) |
| wide | max-w-4xl (896px) |
| full | w-full (no max-width) |
| custom | inline style: max-width: Npx |