Skip to content
Twilight React Playground
ثيم رائدaren

HookName

enumBeginnerserverbrowserlive demo

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

Every HookName member with its string, and the handlers registered under it on this page right now.Try this: pick body: the handlers there are the engine defaults, registered when the hooks module was first imported.
Storefront canvas · ar · RTL
HookName.string valuehandlers now (priorities)
BODY_STARTbody:start100, 90, 80
BODY_ENDbody:end100, 90
BODY_INNERbody:inner
HEAD_STARThead:start
HEAD_ENDhead:end
HEAD_INNERhead:inner
HEADER_STARTheader:start
HEADER_ENDheader:end
FOOTER_STARTfooter:start
FOOTER_ENDfooter:end
PRODUCT_DESCRIPTION_STARTproduct:single.description.start
PRODUCT_DESCRIPTIONproduct:single.description
PRODUCT_DESCRIPTION_ENDproduct:single.description.end
PRODUCT_FORM_STARTproduct:single.form.start
PRODUCT_FORM_ENDproduct:single.form.end
CART_ITEMS_STARTcart:items.start100
CART_ITEMS_ENDcart:items.end
CART_SUMMARY_STARTcart:summary.start
CART_SUMMARY_ENDcart:summary.end
SEARCH_STARTsearch:start
SEARCH_ITEMS_STARTsearch:items.start
SEARCH_ITEMS_ENDsearch:items.end
SEARCH_ENDsearch:end
HOMEPAGE_SLIDERhomepage:slider
HOMEPAGE_BANNERShomepage:banners
HOMEPAGE_FEATUREDhomepage:featured
Controls
What a theme writes
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

app/components/layout/ThemeHeader.tsx
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 HookSlot for 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 by TwilightProvider inside the page body (not inside <head>), with ssr. It also renders a slot named head.
    • HEADER_START, HEADER_END: the engine Header.
    • FOOTER_START, FOOTER_END: the engine Footer, which also renders copyright with 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 renders cart:submit.start and cart:submit.end.
    • BODY_INNER, HEAD_INNER, all four SEARCH_* and all three HOMEPAGE_*: 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:end
    • product: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:end
    • blog:*, brands:index.items.start/.end, thank-you:* and customer:* 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, /types and /types/hooks.

Gotchas

  • On the engine product page the three PRODUCT_DESCRIPTION* names are rendered twice: once by ProductPage with no context, and once inside ProductDetails with { product }. A handler renders in both places unless it returns null when product is missing, as the reference theme's DigitalFilesSettings handler does.

  • docs/06-hook-system.md lists { product } as the context of PRODUCT_FORM_* and { cart } for every CART_* name. In the code only PRODUCT_DESCRIPTION* (inside ProductDetails) get { product }, and only CART_ITEMS_START gets context, named cartItems.

  • Registering on a name no component renders (SEARCH_*, HOMEPAGE_*, *_INNER) shows nothing and reports nothing. Render a HookSlot for it in your theme first.

Related

Source and docs