Skip to main content
@juo/blocks ships a small set of custom elements that connect block definitions to the page. These are the only @juo/blocks APIs called from HTML; everything else (services, contexts, signals) lives in JavaScript.
ElementRole
<juo-context-root>Boundary for context providers and requests.
<juo-page>Root page renderer. Resolves a route through ThemeState and renders the resulting block.
<juo-extension-root>Slot fan-out. Renders the children a merchant placed into a named slot.
<juo-block>Declarative ad-hoc instance of a registered block, parameterized by HTML attributes.
<juo-text>Inline text node. Read-only at runtime; editable inline in the editor.
Two builds ship the same tag set:
  • @juo/blocks/web-components/runtime — lean storefront elements.
  • @juo/blocks/web-components/editor — same elements plus selection wrappers, drop zones, and inline-text editing.
Import one or the other from the portal entry. See Editor for how the editor build layers on top.

<juo-context-root>

Hosts the context tree. Providers call provideContext against it; blocks below it call injectContext and the request is answered by the nearest root above the requester.
<juo-context-root>
  <!-- everything below resolves contexts from this root -->
</juo-context-root>
Nest a second root to override services for a sub-tree. See Scoping and overrides for the pattern and the typical uses (previews, workflow overlays, tenant scoping).

<juo-page>

The root page renderer. Subscribes to ThemeStateContext, asks it to resolve a route, and renders the resulting first block reactively. When the theme state’s blocks change — because the editor updated a prop, a translation flipped, or the route changed — the page re-renders automatically.
<juo-context-root>
  <juo-page route="/"></juo-page>
</juo-context-root>
Attributes
  • routeoptional. When set, the element calls themeState.resolve(surface, route) as soon as a ThemeState becomes available, and again whenever the attribute changes. Omit it to drive resolution from JavaScript (e.g. from a custom router).
  • surfaceoptional. "page" (default) or "overlay". Selects which ThemeState surface to render.
<juo-page> does not take a block name — what it renders is whatever ThemeState.resolve() returns for the given path. That keeps routing in one place (RouterService / theme state) instead of duplicated across markup.

<juo-extension-root>

Renders the children placed into a named slot of the surrounding page’s root block. Page renderers emit one <juo-extension-root> per slot they expose; the editor decorates each child with selection chrome via a wrapper.
// Inside a page block's renderer
const container = document.createElement("div");
container.innerHTML = `
  <juo-extension-root name="header"></juo-extension-root>
  <juo-extension-root name="main"></juo-extension-root>
`;
return container;
Attributes
  • namerequired. The slot name to render. Must match a key in the page block’s slots schema.
  • surfaceoptional. "page" (default) or "overlay". Selects which surface to read children from.
<juo-extension-root> reads from the same ThemeState that <juo-page> resolves, so nothing needs to be passed through props — child blocks just appear when the merchant places them in that slot.

<juo-block>

Mounts a fresh ad-hoc instance of a registered block, parameterized by its HTML attributes. Useful for dropping a block into static markup — a one-off mount in a sidebar, a Storybook story, a hand-written page that isn’t driven by theme state.
<juo-block name="SubscriptionList"></juo-block>
Attributes
  • namerequired. The registered block name (the value passed to defineBlock/registerBlock).
  • Any other attribute is forwarded as a prop on the block’s instance.
<juo-block> is not what renders pages from theme state — for that use <juo-page>. Use <juo-block> only to drop a block by name into markup independently of routing or stored slot composition.

<juo-text>

An inline text node. In the runtime build it’s a passive container — it shows its text content. In the editor build it becomes editable inline, with its content flowing through the theme state’s translation catalog.
<juo-text key="cta-label">Manage subscription</juo-text>
Block renderers typically emit <juo-text> for any merchant-editable copy. See Localization for how the key resolves against the active locale’s catalog.

Composition

A typical page rendered through the platform looks like:
<juo-context-root>           ← services and ThemeState provided here
  <juo-page route="/">       ← resolves the route, renders the root block
    <div>                    ← the root block's renderer output
      <juo-extension-root name="header"/>   ← slot fan-out
      <juo-extension-root name="main"/>
    </div>
  </juo-page>
</juo-context-root>
The root block, the children in each slot, the prop values, and the translations all come from the same ThemeState — the only thing pinned in markup is the route. Editing in the Juo Editor updates the theme state; everything below <juo-page> re-renders automatically. <juo-block> doesn’t appear in this tree because page rendering goes through theme state, not through ad-hoc instances. Use <juo-block> only for a one-off mount that is not part of an editable page.