Components API
Streak ships four built-in components. All are imported from streak/components:
import { WidgetPlaceholder, Script, Dynamic, Preload } from "streak/components"; WidgetPlaceholder
Marks the position in a layout where a widget's rendered HTML will be injected at build time.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Must match the widget id in the sitemap and the handler return key |
type | string | Yes | Must match the widget filename in src/widgets/ (case-sensitive) |
dummy | boolean | No | Used inside Dynamic content where a placeholder is needed without a widget binding |
Usage
WidgetPlaceholder is used only inside layout files:
const MainLayout = () => (
<html 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 in the sitemap widgets[] array must have a corresponding WidgetPlaceholder in the layout. The id and type values must match exactly.
Script
Serializes a function body to a string at build time and emits it as an inline <script> tag that executes as an IIFE in the browser.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Must be unique on the page |
options | object | No | Plain JSON-serializable object passed as the second IIFE argument |
children | function | Yes | Function body to serialize — receives (gDom, options) |
Usage
<Script id="banner-init" options={{ color: "#818cf8", delay: 800 }}>
{(gDom: any, options: any) => {
document.getElementById("banner-heading").style.color = options.color;
}}
</Script> The rendered output is an IIFE:
((function(gDom, options) {
document.getElementById("banner-heading").style.color = options.color;
})(window, {"color":"#818cf8","delay":800})); Serialization Rules
| Rule | Detail |
|---|---|
| No closures | The function is converted with .toString(). Outer-scope variables are not captured. |
| No imports | ES module imports inside the function body will not work. Use gDom.loadPackage() instead. |
| Options must be JSON-serializable | The options object is passed through JSON.stringify. Functions, undefined, and circular references are dropped. |
gDom is window | The first argument is window extended with Streak helpers (loadPackage, loadDynamicComponent, onVisible, etc.). |
Dynamic
Strips its children from the initial HTML at build time. The content is injected on demand at runtime via gDom.loadDynamicComponent().
Props
| Prop | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Identifier used by loadDynamicComponent() to find and inject this content |
children | JSX | Yes | The content to defer — removed from initial HTML, injected on demand |
Usage
Dynamic is always paired with a Script that triggers the injection:
<Dynamic id="nav-submenu">
<ul>
<li>Item A</li>
<li>Item B</li>
</ul>
</Dynamic>
<Script id="nav-submenu-trigger">
{(gDom: any) => {
document.getElementById("nav-btn")
?.addEventListener("click", () => {
gDom.loadDynamicComponent("nav-submenu", () => {
console.info("submenu injected");
});
});
}}
</Script> How It Works
- Build time: children are stripped. A placeholder element is emitted with
component-type="__dynamicWidgetType__"andcomponent-id="<id>". - Runtime:
gDom.loadDynamicComponent(id, callback)finds the placeholder, injects the original HTML, and callscallback.
Dynamic vs Lazy Comparison
| HTML in initial payload | JS loaded | Triggered by | |
|---|---|---|---|
loadingStrategy: "lazy" | Yes | After page is interactive | Automatic |
<Dynamic> | No | On demand | gDom.loadDynamicComponent() |
Use lazy when the HTML should be visible immediately but JS can wait. Use Dynamic when the HTML itself should be excluded from the initial payload.
Preload
Renders a <link rel="preload"> tag. Tells the browser to begin fetching a resource before the parser discovers it naturally.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
href | string | Yes | URL of the resource to preload |
as | "image" | "font" | "style" | "script" | "video" | Yes | Resource type hint for the browser |
media | string | No | CSS media query string; use "" for unconditional preload |
Usage
Use Preload inside a widget that renders in <head>:
<Preload href="/images/hero.jpg" as="image" media="(min-width: 768px)" />
<Preload href="/styles/tailwind.css" as="style" media="" />
<Preload href="/assets/fonts/inter.woff2" as="font" /> Rendered Output
<link rel="preload" href="/images/hero.jpg" as="image" media="(min-width: 768px)">
<link rel="preload" href="/styles/tailwind.css" as="style" media=""> When to Use
- LCP images — preload the largest above-the-fold image to reduce Largest Contentful Paint time.
- Critical fonts — preload web fonts to avoid flash of unstyled text.
- Above-the-fold CSS — preload stylesheets not discovered early in the document.