Esc

WidgetPlaceholder

WidgetPlaceholder is a build-time component from streak-forge/components used inside layouts. It marks the position where a widget's rendered HTML will be injected.


Import

import { WidgetPlaceholder } from "streak-forge/components";

Usage

<WidgetPlaceholder id="HelloBanner" type="HelloBanner" />

Both props are required — WidgetPlaceholder throws at render time if either is missing:

PropTypeMeaning
idstringMust equal the widget entry's id in the sitemap widgets[] and the handler return key. Any string — it does not have to equal type. Must be unique within the page.
typestringMust equal the widget entry's type in the sitemap and the widget filename in src/widgets/ (case-sensitive)

id and type are independent. type selects which widget file renders; id selects which slice of handler data it receives and which placeholder it fills. They're the same string in most examples only for readability.


How It Works at Build Time

At build/render time:

  1. The layout JSX is rendered to an HTML string, with each WidgetPlaceholder emitted as a <widget id="..." type="..."> marker tag.
  2. Each widget in widgets[] is rendered to its own HTML fragment.
  3. For a widget with no loadingStrategy (or loadingStrategy other than "lazy"), its marker is replaced directly with the widget's rendered HTML — it ships inline in the page.
  4. For a widget with loadingStrategy: "lazy", its marker is replaced with a placeholder <div> (component-placeholder component-type="w" component-id="<id>">) instead, and its HTML/scripts are stored for the client runtime to fetch on demand.

The <widget> marker tag itself never appears in the final output — it's always replaced by either the real HTML or a lazy placeholder.


w-m Widget Registry

After all widgets are assembled, the page also contains a hidden registry element:

<script type="application/json" id="w-m">["HelloMessage"]</script>

w-m holds a plain JSON array of the ids of every lazy widget on the page (widgets with no loadingStrategy are not listed here — they're already inline in the HTML). Streak's client runtime reads this array on page load and fetches each lazy widget's HTML in order, one after another.


Example Layout Usage

import { WidgetPlaceholder } from "streak-forge/components";

const MainLayout = () => {
  return (
    <html dir="ltr" lang="en">
      <head>
        <WidgetPlaceholder id="PageHead" type="PageHead" />
      </head>
      <body>
        <WidgetPlaceholder id="HelloBanner"  type="HelloBanner"  />
        <WidgetPlaceholder id="HelloMessage" type="HelloMessage" />
      </body>
    </html>
  );
};

export default MainLayout;

Every widget declared in streak.sitemap.jsonwidgets[] must have a corresponding WidgetPlaceholder in the layout whose id and type match that entry's id and type (case-sensitive). This is a match between the placeholder and the sitemap entry — it does not mean id must equal type.


Reusing a Widget Type Multiple Times

The same type can appear more than once on a page — each occurrence just needs its own unique id:

"widgets": [
  { "id": "featuredProducts", "type": "ProductList" },
  { "id": "newArrivals",      "type": "ProductList" }
]
<WidgetPlaceholder id="featuredProducts" type="ProductList" />
<WidgetPlaceholder id="newArrivals"      type="ProductList" />
// handler return
return {
  featuredProducts: { items: [...] },
  newArrivals:      { items: [...] },
};

Both render src/widgets/ProductList.tsx, each with its own props.data, placed at its own slot.

Caveat — <Script> ids. Script ids are deduplicated per page. If ProductList contains <Script id="product-list">, only the first instance's script (with the first instance's options) ships; later instances get no script. For a widget type used more than once, derive the <Script id> from the data (or accept a prop) so each instance emits a distinct id.