HookName
The predefined slot names as a TypeScript enum, so a typo fails to compile instead of silently filling a slot nobody renders.
import { HookName } from '@salla.sa/twilight-theme-engine/hooks';In plain words
Slot names are plain strings, and a mistyped string fails silently: nothing appears, and nothing tells you why. HookName lists the predefined names so your editor can autocomplete them. HookName.BODY_END is exactly the string 'body:end', and either spelling works everywhere.
An enum is a fixed set of named values. This one also exists when the theme runs, so Object.values(HookName) gives you every name.
Signature
enum HookName {
BODY_START = 'body:start', BODY_END = 'body:end', BODY_INNER = 'body:inner',
HEAD_START = 'head:start', HEAD_END = 'head:end', HEAD_INNER = 'head:inner',
HEADER_START = 'header:start', HEADER_END = 'header:end',
FOOTER_START = 'footer:start', FOOTER_END = 'footer:end',
PRODUCT_DESCRIPTION_START = 'product:single.description.start',
PRODUCT_DESCRIPTION = 'product:single.description',
PRODUCT_DESCRIPTION_END = 'product:single.description.end',
PRODUCT_FORM_START = 'product:single.form.start', PRODUCT_FORM_END = 'product:single.form.end',
CART_ITEMS_START = 'cart:items.start', CART_ITEMS_END = 'cart:items.end',
CART_SUMMARY_START = 'cart:summary.start', CART_SUMMARY_END = 'cart:summary.end',
SEARCH_START = 'search:start', SEARCH_ITEMS_START = 'search:items.start',
SEARCH_ITEMS_END = 'search:items.end', SEARCH_END = 'search:end',
HOMEPAGE_SLIDER = 'homepage:slider', HOMEPAGE_BANNERS = 'homepage:banners',
HOMEPAGE_FEATURED = 'homepage:featured',
}Try it live
| HookName. | string value | handlers now (priorities) |
|---|---|---|
BODY_START | body:start | 100, 90, 80 |
BODY_END | body:end | 100, 90 |
BODY_INNER | body:inner | – |
HEAD_START | head:start | – |
HEAD_END | head:end | – |
HEAD_INNER | head:inner | – |
HEADER_START | header:start | – |
HEADER_END | header:end | – |
FOOTER_START | footer:start | – |
FOOTER_END | footer:end | – |
PRODUCT_DESCRIPTION_START | product:single.description.start | – |
PRODUCT_DESCRIPTION | product:single.description | – |
PRODUCT_DESCRIPTION_END | product:single.description.end | – |
PRODUCT_FORM_START | product:single.form.start | – |
PRODUCT_FORM_END | product:single.form.end | – |
CART_ITEMS_START | cart:items.start | 100 |
CART_ITEMS_END | cart:items.end | – |
CART_SUMMARY_START | cart:summary.start | – |
CART_SUMMARY_END | cart:summary.end | – |
SEARCH_START | search:start | – |
SEARCH_ITEMS_START | search:items.start | – |
SEARCH_ITEMS_END | search:items.end | – |
SEARCH_END | search:end | – |
HOMEPAGE_SLIDER | homepage:slider | – |
HOMEPAGE_BANNERS | homepage:banners | – |
HOMEPAGE_FEATURED | homepage:featured | – |
import { HookName, hookRegistry } from '@salla.sa/twilight-theme-engine/hooks';
// HookName.BODY_END === 'body:end': both spellings name the same slot.
const names = Object.values(HookName);
const registered = names.filter((name) => hookRegistry.has(name));
Example
import { HookName, HookSlot } from '@salla.sa/twilight-theme-engine/hooks';
export function ThemeHeader({ children }: { children: React.ReactNode }) {
return (
<header className="theme-header">
<HookSlot name={HookName.HEADER_START} />
{children}
<HookSlot name={HookName.HEADER_END} />
</header>
);
}
How it behaves
Where the engine renders each name. A name renders only where some component places a
HookSlotfor it, so if your theme replaces a component, its slots go with it.BODY_START,BODY_END:TwilightProvider, before and after the layout, on every page. The engine's default handlers hang here.HEAD_START,HEAD_END:WidgetHead, rendered byTwilightProviderinside the page body (not inside<head>), withssr. It also renders a slot namedhead.HEADER_START,HEADER_END: the engineHeader.FOOTER_START,FOOTER_END: the engineFooter, which also renderscopyrightwith context{ storeName }and the standard copyright line as its fallback.PRODUCT_DESCRIPTION_START,PRODUCT_DESCRIPTION,PRODUCT_DESCRIPTION_END: the product page, twice each (see Gotchas).PRODUCT_FORM_START,PRODUCT_FORM_END:ProductPage, around the add-to-cart form, with no context.CART_ITEMS_START:CartPage, with context{ cartItems }.CART_ITEMS_END:CartPage, no context.CART_SUMMARY_START,CART_SUMMARY_END:CartSummary, no context. It also renderscart:submit.startandcart:submit.end.BODY_INNER,HEAD_INNER, all fourSEARCH_*and all threeHOMEPAGE_*: no engine component renders them.
Engine pages also render names that have no enum member, for example:
product:start,product:details.start/.end,product:related.start/.end,product:endproduct:list.start,product:list.items.start/.end,product:list.end(listing and search)home:start,home:content,home:end;cart:start,cart:end;notfound:start,notfound:content,notfound:endblog:*,brands:index.items.start/.end,thank-you:*andcustomer:*slots on those pages
A regular (non-const) string enum: it exists at runtime and has no reverse mapping. The same enum is exported from
@salla.sa/twilight-theme-engine,/typesand/types/hooks.
Gotchas
On the engine product page the three
PRODUCT_DESCRIPTION*names are rendered twice: once byProductPagewith no context, and once insideProductDetailswith{ product }. A handler renders in both places unless it returnsnullwhenproductis missing, as the reference theme'sDigitalFilesSettingshandler does.docs/06-hook-system.md lists
{ product }as the context ofPRODUCT_FORM_*and{ cart }for everyCART_*name. In the code onlyPRODUCT_DESCRIPTION*(insideProductDetails) get{ product }, and onlyCART_ITEMS_STARTgets context, namedcartItems.Registering on a name no component renders (
SEARCH_*,HOMEPAGE_*,*_INNER) shows nothing and reports nothing. Render aHookSlotfor it in your theme first.
Related
A named empty place in the page that renders every handler registered under its name, plus a spot where Salla apps inject content.
hookRegistryThe one shared list of slot handlers: register content under a slot name, read or clear it, or register many at once with defineHooks.
registerDefaultHooksRegisters the engine's built-in slot handlers (GTM, bundles, store closed, fast checkout, price quote, pre-order); importing the hooks module already runs it.