@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.
| Element | Role |
|---|---|
<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. |
@juo/blocks/web-components/runtime— lean storefront elements.@juo/blocks/web-components/editor— same elements plus selection wrappers, drop zones, and inline-text editing.
<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-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.
route— optional. When set, the element callsthemeState.resolve(surface, route)as soon as aThemeStatebecomes available, and again whenever the attribute changes. Omit it to drive resolution from JavaScript (e.g. from a custom router).surface— optional."page"(default) or"overlay". Selects whichThemeStatesurface 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.
name— required. The slot name to render. Must match a key in the page block’sslotsschema.surface— optional."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.
name— required. The registered block name (the value passed todefineBlock/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> 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: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.