npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@alpaca-headless/dynamic-renderings

v1.0.4697

Published

Optional Alpaca Headless dynamic renderings: source client, safe React rendering, client code and Next.js integration.

Readme

@alpaca-headless/dynamic-renderings

Optional dynamic renderings for Alpaca Headless: React components stored as source on Sitecore items, compiled and run by the host itself through @alpaca-headless/dynamic-renderings-engine (compile threads and QuickJS render threads inside the head's process), and turned back into React. Design and threat model: Headless/dynamic-renderings.md in the Alpaca repository. No Parhelia installation is required; a head the Parhelia editor serves adds the /parhelia subpaths (see below).

Requirements

  • React Server Components. The Alpaca SDK's Placeholder is an async server component, so every Alpaca head is an RSC app. Render trees become React elements on the server only; the browser gets a rendering's client code only where it may run (see Client code).
  • Node.js server runtime, Node 22.13 or later: the engine uses worker threads.
  • React 19.2 or later (>=19.2.0 <20).
  • Next.js for the ready-made adapter (/next, /next/config). Other RSC frameworks can mount the framework-neutral handlers (/handlers) at the same paths and must load the engine unbundled.

Adopting it in a Next.js head

  1. Dependencies: @alpaca-headless/dynamic-renderings and @alpaca-headless/dynamic-renderings-engine (same version).

  2. src/dynamic.config.ts, the one configured instance for the page and the routes:

    import { defineDynamicRenderings } from "@alpaca-headless/dynamic-renderings/server";
    import { nextThemeFile } from "@alpaca-headless/dynamic-renderings/next";
    
    export const dynamicConfig = defineDynamicRenderings({
      theme: nextThemeFile("src/app/[[...path]]/theme.css"),
    });
  3. The build route, at exactly this path (the CM calls it when a rendering is checked or saved):

    // src/app/api/alpaca/renderings/build/route.ts
    import { buildRoute } from "@alpaca-headless/dynamic-renderings/next";
    import { dynamicConfig } from "../../../../../dynamic.config";
    export const { POST } = buildRoute(dynamicConfig);

    A root .gitignore with a build/ rule hides it; add !src/app/api/alpaca/renderings/build/ (or the head's path to it).

  4. The client bundle route, at exactly this path (pages name it in their bundle URLs):

    // src/app/api/alpaca/renderings/client/[file]/route.ts
    import { clientRoute } from "@alpaca-headless/dynamic-renderings/next";
    import { dynamicConfig } from "../../../../../../dynamic.config";
    export const { GET } = clientRoute(dynamicConfig);
  5. next.config.js:

    const { withDynamicRenderings } = require("@alpaca-headless/dynamic-renderings/next/config");
    module.exports = withDynamicRenderings(nextConfig);

    It loads the engine unbundled (serverExternalPackages: its threads start from files next to it), traces the engine and src/app/**/theme.css into standalone output (withDynamicRenderings(nextConfig, { themeFile }) for another location), and keeps a catch-all CORS rule off /api/alpaca/*, also under a prefix. When a proxy serves the head's pages under a path prefix, pass its patterns as routePrefixes (e.g. ["/editor/proxy/:host"]): bundle URLs under them are rewritten to the bundle route. The head's own settings are kept.

  6. The page:

    const renderer = createRenderer(dynamicConfig); // from "@alpaca-headless/dynamic-renderings/react"
    
    const request = await readRequest(dynamicConfig, params, searchParams);   // before the layout call
    const routeData = await resolveRoute({ path: request.route, searchParams: request.searchParams, ..., apiConfig: request.identity });
    // ...redirects, errors, the head's own edit-mode context override (request.editing)...
    const dynamic = await renderer.attach(context, routeData.result.page, request);
    // render with dynamic.page and dynamic.context, never routeData's:
    {dynamic.head}
    {renderer.frame(dynamic, <Placeholder name="ph-content" content={dynamic.page} context={dynamic.context} />)}

    renderer.frame puts the tenant's theme tokens in front of the page body. dynamic.head holds what client code needs on the page. The page must not be served from a page cache while a rendering is still being compiled (dynamic.state.preparing); reading the request makes the route dynamic already.

The CM side uses the Alpaca Site Root, its Headless API key and Rendering Host settings, and ALPACA_RENDERING_BUILD_KEY (or the CM setting Alpaca.Headless.DynamicRenderings.BuildKey). The same secret must be configured on the CM and host. See Headless/dynamic-renderings.md. Nothing else runs next to the head.

Client code

A rendering's Client Code is imported by its code as ./client. Each client component the server code renders arrives as an island: a first paint the engine rendered in QuickJS (initial state, no effects), sanitized like any markup, with the server code's children in its place. Whether the browser also loads the real component is decided per page:

| Page | Client code | | --- | --- | | a sealed request (requestAuth recognised it) | every island loads, inside an opaque-origin frame only | | anything else, edit mode included | islands of renderings the CM reports as approved |

A loaded component always gets editing: false, in the edit canvas too; the first paint renders with editing: true.

A bundle URL is {prefix}/api/alpaca/renderings/client/{clientHash}-{compiledFormat}.js?id={renderingId}&database={db} (plus &page={pageId} when sealed), where clientHash is the first 16 hex digits of the SHA-256 of the LF-normalized client code. The path is kept short because a proxy prefix with a token may precede it and the CM limits URL paths to 260 characters. The URL names everything needed to build the bundle again, so any process serves it: from its compile cache when the bundle is approved, otherwise after asking the CM for that rendering (approved ones only without a page; a sealed request asks with its page and its own identity). A client code that no longer matches the URL's hash answers 404, and a cached bundle is reused only for exactly the code the CM returned; misses are remembered for a minute. Approved bundles are public, immutable, sealed ones private, no-store. The bundles take React from globalThis.__alpaca_dynamic_react, which dynamic.head sets on pages that load them.

Editor integrations configure the rest in defineDynamicRenderings:

  • requestAuth(headers): returns the CM identity of a sealed request (headers only, no cookies); such a page is always a preview.
  • transformPage(page, { sealed, prefix }): adjusts the page data before it renders, e.g. media URLs a sealed frame cannot load.
  • editor.clientCodeHintAttribute: in preview mode, dynamic.head lists the renderings with client code in <script type="application/json" {attribute}="{id},...">.

Heads the Parhelia editor serves

Everything Parhelia-specific lives under /parhelia: the editor's markers, the CM's proxy prefixes (/parhelia/proxy/{name}/..., /parhelia/preview/{token}/{name}/...) and the sealed "Run client code" preview (its token as the request's identity, and its media). Use its defineDynamicRenderings in place of the one from /server, and its withDynamicRenderings:

// src/dynamic.config.ts
import { defineDynamicRenderings } from "@alpaca-headless/dynamic-renderings/parhelia";

// src/app/api/parhelia/media/[code]/[...path]/route.ts
import { previewMediaRoute } from "@alpaca-headless/dynamic-renderings/parhelia/next";
export const { GET } = previewMediaRoute(dynamicConfig);
// next.config.js
const { withDynamicRenderings } = require("@alpaca-headless/dynamic-renderings/parhelia/next/config");

Everything else (readRequest, buildRoute, clientRoute, createRenderer) comes from the subpaths below as in any head.

Subpaths

| Subpath | Contents | Loads in | | --- | --- | --- | | /protocol | limits, node types, allowed imports, compiler format, types | plain Node | | /css-policy | the stylesheet policy shared with the engine | plain Node | | /rich-text | the rich-text sanitizer | plain Node | | /server | defineDynamicRenderings, CM client, engine client and compile cache, page preparation, sanitizer | server (server-only) | | /react | createRenderer, render-tree conversion, trusted components | server components | | /client | the island loader and the React global for client code bundles | browser ("use client" per file) | | /handlers | the framework-neutral build and client bundle handlers | server | | /handlers/rewrites | the CORS exclusion source and the bundle route rewrites | plain Node | | /next | readRequest, requestIdentity, buildRoute, clientRoute, nextThemeFile | Next.js server | | /next/config | withDynamicRenderings (CommonJS and ESM) | plain Node | | /parhelia | the Parhelia editor preset: defineDynamicRenderings, the preview token's requestAuth, rewriteMediaUrls, splitProxyPrefix, handlePreviewMedia | server | | /parhelia/next | previewMediaRoute | Next.js server | | /parhelia/next/config | withDynamicRenderings with the Parhelia CM's proxy prefixes and the preview media rewrite | plain Node |

Versions

The head compiles and runs renderings itself, so the two packages always come from the same release: install them at the same version. The build route reports the compiler format (compiledFormat, 3 since client code), so the CM can tell whether a head runs client code.