Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Glossary

Every term this book uses as a term of art, in one alphabetical list. Each entry is a one-line orientation plus the page that owns the term — the rule, the limit and the error string live there, never here. When this page and a linked page disagree, the linked page wins.

Version gates are shown inline, e.g. (0.11.0+), and mean @homerunner-next/widget-core. Which versions you can actually install is in Install and versions.

TermWhat it isDefined in
acceptsPer-slot list of categories and/or exact keywords the dashboard’s slot picker offers. A hint to the customer, read by nothing at render time.Layout widgets
Advisory (unvalidated)Enforcement level: nothing checks it at any layer, so a typo is yours to find in production.Limits and error index
Asset proxy (/p/){proxy}/p/{slug}/{dist-relative file} — the versionless plugin asset endpoint. One 302 to the content-hashed CDN file. Never put crossorigin on a /p/ script tag.Keywords, assets and URLs
assets.fontsStylesheet URLs (or confined relative paths) injected at document level, because @font-face is ignored inside a shadow root. (0.12.0+)Keywords, assets and URLs
Build manifestdist/manifest.json — generated {buildHash, generatedAt, files} mapping every dist-relative name to its hashed twin. Not the registration manifest.Keywords, assets and URLs
Built zipThe zip the reviewer builds from your source and uploads to the CDN — your source plus the whole dist/ tree.Packaging and publishing rules
Bundled assetAn image or font your widget imports; above the inline threshold the SDK emits it as a real file and resolves its URL from the bundle’s own location. (0.12.0+)Keywords, assets and URLs
categorycontent or layout on a widget summary. Anything but the literal layout resolves to content.Manifest: widget summary
Composed pageThe customer page a worker assembles from the renderer’s HTML and caches. Your widget is one island inside it.How a widget renders
Confined relative pathA manifest path that cannot escape the published version prefix: no leading /, no scheme:, no \, no ...Manifest: widget summary
configSchemaThe JSON Schema in your manifest (or sub-manifest) that drives the dashboard form. Emitted by zodToManifestSchema().Config schema and UI schema
Content widgetA widget that renders itself and hosts nothing — the default category.Manifest: widget summary
crossorigin trapAdding crossorigin="anonymous" to a /p/ tag: the off-host 302 target sends no ACAO, so the bundle silently never executes.Keywords, assets and URLs
CSR-only widgetA widget declaring "ssr": false — no SSR bundle, no ssr-entry.ts, mounted in the browser only. Give it an expectedHeight.Manifest: widget summary
data-hr-errorHost-element marker naming why a widget failed: mount-failed, render-failed, rate-limited, widget-fetch-failed, property-not-found.Runtime, mount and the DOM contract
data-hr-schemeHost attribute meaning “follow the OS” — a pre-hydration CSS gate only, stamped by the server and removed the moment the runtime attaches.Styling and theming
data-hr-ssr-idServer-written key into the page’s props registry, and the identifierPrefix used to hydrate. Layout shells never get one.Runtime, mount and the DOM contract
data-hr-themeHost attribute carrying the resolved theme once JS owns theming. Your dark rules must target it, and the media-query arm must yield to it.Styling and theming
Declarative Shadow DOM (DSD)Server-emitted <template shadowrootmode="open">, so the browser attaches and styles your shadow root while parsing — no mount blink.How a widget renders
Degraded pageA server-rendered page carrying any failure breadcrumb or failed prefetch. It still serves; its cache lifetime collapses.Limits and error index
dehydrateStateOptional SSR export: prefetch into the page’s shared React Query client so the browser hydrates without refetching.Component and SSR module
deprecatedSoft retirement of one suite widget: gone from the picker, existing placements keep rendering. Not a kill switch.Manifest: widget summary
Dev panelThe bar npm run dev pins to the page — scenario, theme, latency and a settings textarea driving window.__HR_MOCK__. In a suite it takes a keyword list and pushes to all of them.Dev sandbox and mocking
Dev sandboxnpm run dev (:3001): your widget — or every widget of a suite, with the layout composed — mounted against an MSW-mocked HomeRunner API.Dev sandbox and mocking
dist-relative nameThe normalised form every consumer reduces an asset reference to: everything after the last dist/, else the basename.Keywords, assets and URLs
Elevated portalA portal target with its own shadow root at document.body, for dialogs the widget’s own box would clip. Needs its tokens passed in.Portals and dialogs
Embed snippetThe copyable HTML for a standalone placement: the runtime script, your bundle, and a host div carrying the keyword.Runtime, mount and the DOM contract
Enforcement levelOne of exactly four: publish ERROR, publish warning, silent runtime truncation, advisory (unvalidated). Every rule in the book carries one.Limits and error index
{env} prefixprod, dev or local — the mandatory first segment of every published URL. An example URL without it is wrong and 404s.Keywords, assets and URLs
Evergreen runtimeSince 0.10.0 React, React DOM, React Query and @homerunner-next/widget-core/runtime are externalized onto window.HRWidgetRuntime, so platform mount fixes reach your published bundle with no rebuild.Runtime, mount and the DOM contract
expectedHeightAnti-CLS hint in CSS pixels on a widget summary. Emitted as data-hr-min-height for CSR-only widgets and baked into embed snippets.Manifest: widget summary
externalFetchThe host allow-list your server bundle may fetch beyond HomeRunner’s public API. Unvalidated at publish; enforced only in the sandbox.Component and SSR module
Failure nodeThe hidden data-hr-widget-error breadcrumb the renderer substitutes for a widget that could not render. It degrades the page; it does not break it.Component and SSR module
FeedOne customer site’s content scope. Plugins are installed per feed, and every widget row belongs to exactly one.Install, customers and kill switches
filter.propertyThe property the page (SSR) or the layout (CSR) pushes into every widget’s settings, so a slot child knows which listing it is on.Composing a page
Fixturesmocks/fixtures.ts — per-endpoint overrides for the dev sandbox’s generated data, keyed by endpoint. In a suite the widget factory is what gives each mounted widget its own keyword.Dev sandbox and mocking
getFinalSettingsThe platform’s merge that produces the settings blob your component receives. Precedence chain and __parentColorScheme live with it.Settings and options
getInitialDataOptional SSR export whose return value becomes props.data and is replayed to the browser. Must be JSON-serialisable.Component and SSR module
getStaticAssetsOptional SSR export returning {css, js}. Prefer omitting it — everything it returns is re-resolved, and exporting it drops your declared fonts from <head>.Component and SSR module
Hashed twinThe {base}-{sha8}{ext} copy the build writes beside every dist/ file, recorded in the build manifest and served immutably.Keywords, assets and URLs
hr-widget-buildThe SDK’s build CLI: validates every summary, then runs the (widget × pass) matrix and finalises the manifests. Never run bare vite build on a suite. (0.11.0+)SDK reference
HRPluginswindow.HRPlugins[registryKey] = {component, category?} — the page-global registry your client bundle writes to.Runtime, mount and the DOM contract
HRWidgetRuntimeThe page-level global the runtime IIFE installs: React, React DOM, React Query, widgetCore and the baked base URLs your bundle externalizes against.Runtime, mount and the DOM contract
HydrationAttaching React to server-rendered markup instead of re-creating it, using the props and query cache the server serialised.How a widget renders
IIFE bundleYour client bundle — dist/{slug}.iife.js (v1) or dist/{widget}/{widget}.iife.js (suite).Keywords, assets and URLs
Install / UninstallThe customer’s per-feed lifecycle. Install is Activate; Uninstall is refused while widgets built from your plugin still exist.Install, customers and kill switches
KeywordThe single string that addresses a widget everywhere: plugin:{slug} (v1), plugin:{slug}:{widget} (suite), bare for a system widget.Keywords, assets and URLs
Kill switchOne of three subtractive switches that withhold your code: platform suspension, platform per-widget, per-feed per-widget. Nothing is deleted from the CDN.Install, customers and kill switches
Layout shellThe layout’s own markup around its slots. On a server-rendered page it is static light DOM with no shadow root and no mount — never interactive.Layout widgets
Layout widgetA suite widget with category: "layout" that hosts other widgets in named slots. Needs an SSR bundle, a slots config field and a registered category. (0.11.0+)Layout widgets
LayoutWidgetPropsWidgetProps plus renderedSlots. Treat widgetType and target as optional — SSR supplies neither.Layout widgets
ManifestRoot manifest.json — the registration document central caches and ships with every render.Manifest: root fields
Manifest shapeSingle-widget (v1) or suite (widgets[]). The discriminator is one line, and the choice is frozen at first publish.The two manifest shapes
Manifest URLWhere your published manifest lives: {cdn}/{env}/{slug}/{version}/manifest.json. The {env} segment is never optional.Keywords, assets and URLs
MarketplaceThe Marketplace tab of a feed’s Plugins page — the only catalogue customers see. There is no global plugin browser.Install, customers and kill switches
Media referenceA media/<file> path in the manifest (icon, cover, screenshots, readme). The rules differ per field, and media/ resolves only after a publish.Manifest: root fields
mediaSafeAutoOpts a widget into parse-time auto scheme stamping. It is a promise about your CSS that nothing validates. (0.12.0+)Manifest: widget summary
mount(container, config)The runtime entry your bootstrap calls. Selects [data-hr-widget="{keyword}"] inside the container, hydrates or client-renders each match. Returns void.Runtime, mount and the DOM contract
Namespaced keywordThe suite form plugin:{pluginSlug}:{widgetSlug}, split at the first colon.Keywords, assets and URLs
options (raw)The settings blob your component receives. It is never zod-parsed for plugins, on either side — call parseWidgetConfig yourself, everywhere.Settings and options
options-updatedThe DOM event the dashboard preview dispatches to re-render a mounted widget with new settings.Runtime, mount and the DOM contract
Page assignmentThe per-feed mapping (Feed → Page URLs → Page layouts) of a server-rendered page to a layout. Absent means Automatic.Layout widgets
Page layoutA layout currently assigned to one of a feed’s server-rendered pages, rendered with stored bindings only.Layout widgets
pagesThe layout-only summary field declaring which pages the layout may be assigned to. (0.12.0+)Layout widgets
parseWidgetConfigThe SDK call that strips null leaves, coerces stringified scalars, lifts legacy spacing and applies your schema. The only real gate on stored settings.Config schema and UI schema
PlaygroundThe dashboard page for one widget instance: settings form, live preview, embed code and — for a layout — the Slot Configuration panel.Config schema and UI schema
PluginOne React component (or up to 24 of them) plus SSR modules and a manifest, submitted as a source zip and published by HomeRunner. You host nothing.Anatomy of a plugin
PreflightThe checks you run yourself before zipping — including the reviewer-only gates you would otherwise fail blind.Preflight and submit
prefillPer-slot list of system widget keywords the dashboard binds at creation time, so a new layout opens populated. Creation-time only. (0.12.1+)Layout widgets
PresetA root-level one-click composition: a layout, its children and their slot bindings, created atomically from the widget picker. Max 8. (0.11.0+)Layout widgets
Props registrywindow.HRWidget.__WIDGET_PROPS__, keyed by data-hr-ssr-id — the exact props the server rendered with, replayed at hydration.Runtime, mount and the DOM contract
Public API/public-api/v1 — the read-mostly HomeRunner API your widget fetches from, client-side and inside the sandbox.Public API
PublishThe admin action that uploads the built zip under a new version prefix, verifies it, repoints the live manifest, then warms and purges. It also un-suspends.Publishing, versions and rollback
registerPlugin / registerPluginWidgetThe two spellings of the same registry write. registerPluginWidget(p, w, C, meta) is registerPlugin("{p}:{w}", C, meta). (suite form 0.11.0+)Runtime, mount and the DOM contract
Registry keyEverything after the plugin: prefix — {slug} or {slug}:{widget}. The key you register under and the key mount looks up.Runtime, mount and the DOM contract
renderedSlotsThe pre-rendered children a layout receives per slot. SSR omits an empty slot’s key entirely; CSR emits []. Always write renderedSlots?.[name] ?? null.Layout widgets
resolvedThemeThe light/dark prop your component receives. On a server-rendered page an auto widget is handed light and never corrected — branch in CSS, not on this prop.Styling and theming
ReviewA human read of your source zip ending in approved, changes requested or rejected. Nothing notifies you — poll My plugins.From your laptop to a customer page
RollbackRepointing the live version at an earlier published prefix. It does not un-suspend and it cannot target a pre-0.10.0 build.Publishing, versions and rollback
runtime.widgetCoreThe widget-core version your build stamps into the manifest. Generated output — never hand-write it. Publishing requires ≥ 0.10.0.Manifest: root fields
Scaffoldnpx create-hr-plugin <slug> — the non-interactive project generator. --template single (default) writes a v1 project, --template suite a three-widget suite with a layout. Both ship a dev sandbox and an SSR preview.Install and versions
ScenarioOne of the dev sandbox’s six data states — default, empty, single, large, loading, error. Config endpoints are never gated by it.Dev sandbox and mocking
settings.slotsRecord<slotName, widgetId[]> stored on the layout instance — the customer’s bindings, in render order. Not part of the manifest.Layout widgets
Shadow rootThe isolated DOM tree each content widget renders into. Also where your stylesheet is linked, and why @font-face and Radix’s a11y probes behave oddly.Runtime, mount and the DOM contract
shadowDOMRoot-level manifest flag, whole-plugin, no per-widget override. It must agree with what your bootstraps pass to mount().Manifest: root fields
Shared query clientThe single React Query client a rendered page owns. Prefetch into it from dehydrateState; the ["widget", …] and ["feed", …] key spaces are the platform’s.Fetching data
SlotA named region a layout declares in slots[] and renders from renderedSlots.Layout widgets
Slot bindingOne widget id stored under a slot name. Bindings pointing at deleted widgets are dropped at render; nothing prunes them.Layout widgets
Slot editorThe Slot Configuration panel the dashboard injects into a layout’s own form, over your zod slots field.Composing a page
SlugYour plugin’s id, used verbatim as the CDN slug, the keyword stem and the /p/ path segment.Manifest: root fields
Source zipWhat you submit: manifest, package.json, src/, widgets/, media/ — never dist/, node_modules/ or .git/.Packaging and publishing rules
spacingThe base schema’s compound margin/padding/width/maxWidth field (0.9.0+). It replaced the old width: {maxWidth, unit} pair; apply it with resolveWidgetSpacingStyle.Config schema and UI schema
SSRServer-side rendering: the renderer evaluates your SSR bundle, runs the data hooks, and embeds the markup and dehydrated cache into the page.How a widget renders
SSR bundleThe CommonJS file at dist/[{widget}/]{name}-ssr.umd.js. The umd in the name is historical.Component and SSR module
ssr-entry.tsThe re-export barrel the SSR pass compiles. It exists iff the widget’s summary declares ssr.url.Component and SSR module
SSR previewnpm run dev:ssr (:3003) — a local harness for the server path, one widget at a time. A suite names it: --widget {slug} (0.12.2+). It is not the renderer’s envelope; know the divergences before trusting it.Previewing SSR locally
Sub-manifestwidgets/{slug}.manifest.json — one suite widget’s configSchema + uiSchema, generated by your own inject-schema script, fetched by the dashboard only. (0.11.0+)Config schema and UI schema
SubmissionYour source zip on Dashboard ▸ Plugins ▸ My plugins, and the five-state timeline it moves through. Re-submitting replaces whatever is in flight.From your laptop to a customer page
SuiteThe modern manifest shape: up to 24 widgets under one version, one review, one publish. New plugins should start here, even with one widget. (0.11.0+)The two manifest shapes
Suspensionstatus = suspended — the platform-wide kill switch. /p/ 404s every file and every instance becomes a failure node.Install, customers and kill switches
Symbolic component tokenThe uiSchema component name the dashboard resolves to a real form control. The canonical list — and what each must be paired with — is one table.Config schema and UI schema
System widgetA first-party HomeRunner widget, addressed by a bare keyword (gallery, reviews) with no plugin: prefix; its assets are served from /w/, not /p/.Keywords, assets and URLs
uiSchemaThe symbolic UI declaration beside your configSchema. It covers your own fields only; a top-level entry replaces the platform’s control wholesale.Config schema and UI schema
usePortalContainerHook returning the portal node inside your shadow root — the default target for dialogs and tooltips.Portals and dialogs
useShadowHostHook returning your widget’s host element (the [data-hr-widget] div).Portals and dialogs
useTopLevelPortalHook returning a portal target outside every shadow root, for dialogs the widget’s own box would clip. Pass it your token CSS.Portals and dialogs
Version prefix{env}/{slug}/{version}/ — the immutable key prefix every published object lives under. Bumping the version changes the prefix, not the /p/ URL.Keywords, assets and URLs
Visibilitypublic (every account) or private (granted accounts only). Chosen by the reviewer; you cannot set or read it.Install, customers and kill switches
viteHomerunnerWidgetThe Vite preset from @homerunner-next/widget-core/vite that supplies plugins, define, build and server. Drop it into vite.config.ts.SDK reference
vm sandboxThe node:vm context the renderer evaluates your SSR bundle in: seven shared modules, stubbed browser globals, bounded execution. Not a security boundary.Component and SSR module
/w/The system-widget asset path (including runtime.iife.js). Plugin assets never live here; the scaffold’s local server only mirrors the shape.Keywords, assets and URLs
WidgetOne renderable unit on a page. Plugin widgets and system widgets share the same runtime, DOM and settings contracts.Anatomy of a plugin
Widget iconOptional per-widget icon (media/<file> or an absolute URL) shown on picker cards and the plugin page; falls back to the root icon, then a glyph. (0.12.0+)Manifest: widget summary
Widget slugA widget’s id, unique inside its plugin, ^[a-z0-9][a-z0-9-]*$, never dist / widgets / media. It names the dist/ directory and the keyword tail.Manifest: widget summary
Widget summaryOne object in widgets[] — the whole per-widget contract the renderer ever sees. (0.11.0+)Manifest: widget summary
WidgetPropsThe props every widget component receives. Import it from /contracts rather than hand-rolling it; what each path actually supplies differs.Component and SSR module
widgetSchemaThe nine-field base zod schema every widget extends (spacing, colorScheme, filter, lightModeColors, darkModeColors, font, language, section, customCss). The platform renders none of them for you.Config schema and UI schema
widgetTypeA required, non-empty single-widget root field whose value is read by nothing. Routing uses the plugin id.Manifest: root fields

Numbers, caps and verbatim error strings are not repeated here — they live in Limits and error index, and symptoms are indexed in Troubleshooting.