Page frame slots
Slots that wrap every page - the head, body, header and footer.
Use these slots when the content belongs on every page, such as an announcement bar or a tracking snippet.
Where they sit
body:startabove the headerheader:startfirst thing in the headerheader:endlast thing in the headerfooter:startfirst thing in the footercopyrightdefault: the copyright linefooter:endlast thing in the footerbody:endbelow the footerThe three head slots are not drawn, because they output nothing visible.
The head:* slots are not inside the document <head> tag. They render at the top of the app, before body:start, which is why they exist mostly for installed apps' elements. To change the real <head> (title, meta, links), use the route head option instead.
Slot reference
| Slot | Where | Context | Notes |
|---|---|---|---|
head:start | Top of the app, first of the three head slots | twilight | Server-rendered (ssr). |
head | Between head:start and head:end | twilight | Server-rendered (ssr). |
head:end | Last of the head slots | twilight | Server-rendered (ssr). |
body:start | After the head slots, before the layout | twilight | The engine registers defaults here: Google Tag Manager, fraud-check snippet and the store-closed notice (priority 100, 90, 80). |
header:start | First child inside <header class="store-header">, above the merchant's announcement bars and the top bar | twilight | Good for a bar of your own. |
header:end | Last child inside <header>, after the mobile menu | twilight | |
footer:start | First child inside <footer class="store-footer">, above the footer columns | twilight | Not rendered when the footer has inSheet. |
copyright | In the footer's bottom row, before the payment icons | storeName, twilight | Shows the default copyright line while nothing is registered. Any handler replaces that line. |
footer:end | Last child inside <footer>, after the bottom row | twilight | Not rendered when the footer has inSheet. |
body:end | After the layout, at the end of the app | twilight | The engine registers defaults here: its tracking bundles and the fast-checkout script (priority 100, 90). |
header:*, footer:* and copyright are rendered by the engine's Header and Footer components. A layout that replaces them must render these slots itself (the header and footer page explains the swap). body:start and body:end belong to the provider and keep working.
Example: announcement bar
Register it once, in the module your app loads at startup (see Slots):
import { hookRegistry, type HookContext } from '@salla.sa/twilight-theme-engine/hooks';
// Announcement bar above the header content
hookRegistry.register('header:start', ({ twilight }: HookContext) => {
const code = twilight.currency?.code;
return (
<div className="bg-black text-white text-center text-sm py-2">
Free shipping on orders over 200 {code}
</div>
);
});Next
- Slots overview covers priority, fallbacks and installed apps.
- Home and catalog slots are the next area.
- Header and footer shows how to replace them.