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

registerDefaultHooks

functionAdvancedserverbrowserlive demo

Registers the engine's built-in slot handlers (GTM, bundles, store closed, fast checkout, price quote, pre-order); importing the hooks module already runs it.

import { registerDefaultHooks } from '@salla.sa/twilight-theme-engine/hooks';

In plain words

The engine fills a few slots for you: the Google Tag Manager container, Salla's bundles script, a "store closed" notice, the quick-checkout script, a price-quote button and a pre-order notice. Each one checks the store settings when it renders, and renders nothing when its feature is off.

You never call this function. The first time anything imports @salla.sa/twilight-theme-engine/hooks, it runs by itself, and TwilightProvider imports that module. It is exported so you can see what is there.

Signature

function registerDefaultHooks(): void

// What it registers (priority, and when the handler renders something):
// body:start         GoogleTagBody            100  store.settings.keys.gtm has ids
//                    SiftSnippet               90  store.settings.keys.sift
//                    StoreClosedNotification   80  store.settings.opening_hours.enabled
// body:end           TwilightBundles          100  always
//                    fast-checkout <script>    90  store.settings.buy_now
// cart:submit.start  PriceQuoteButton         100  store.settings.features.price_quote
// cart:items.start   PreOrderNotification     100  features['pre-order-campaigns'] and a
//                                                  top-level cart item with has_pre_order_campaign

Try it live

Everything registered in this browser tab. Names without a playground: prefix were registered by the engine, not by the playground.Try this: compare body:start (100, 90, 80) with the store settings under the table: each default is registered whatever the store, and decides at render time.
Storefront canvas · en · LTR
hookRegistry.list()priorities
body:start100, 90, 80
body:end100, 90
cart:submit.start100
cart:items.start100

This store: keys.gtm ["GTM-TGFC6FV"] · buy_now {"label":"Salla","countryCode":"SA","supportedCountries":["SA","BH","KW","OM","QA","AE","JO"],"supportedNetworks":["masterCard","visa"],"validateUri":"/dev-vgckq3fssfhjewwi/checkout/applepay/validate","myfatoorahS2s":false,"currency":"SAR","networks":["masterCard","visa"],"countries":["SA","BH","KW","OM","QA","AE","JO"],"is_digital":false,"merchant_identifier":"salla-internal.salla.sa"}

What a theme writes
import { hookRegistry, HookName } from '@salla.sa/twilight-theme-engine/hooks';

// Importing the hooks module already registered the defaults. Never call registerDefaultHooks() again.
// Add your own content next to them:
hookRegistry.register(HookName.BODY_END, () => <div id="chat-widget" />, 10);

Example

app/hooks/index.tsx
import { hookRegistry, HookName } from '@salla.sa/twilight-theme-engine/hooks';

// The defaults are already registered. Add yours next to them:
// a lower priority renders after them.
hookRegistry.register(HookName.BODY_END, () => <div id="theme-chat-root" />, 10);

// Inspect what is there, highest priority first:
hookRegistry.getHandlers(HookName.BODY_START).map((handler) => handler.priority); // [100, 90, 80]

How it behaves

  • packages/theme-engine/src/hooks/index.ts imports ./registerDefaultHooks for its side effect. TwilightProvider imports MasterLayout, which imports that barrel, so every theme gets the defaults even if it only imports hook subpaths.

  • The handler components are React.lazy with a Suspense fallback of null: their code downloads only when a handler actually renders something.

  • The Sift handler calls useUser() inside the handler, which works because HookSlot renders each handler as its own component.

  • The fast-checkout handler keeps #fast-checkout-js in the body: TanStack removes head scripts after hydration, and salla-add-product-button looks the script up by id when it renders.

  • docs/06-hook-system.md, "Auto-Registered Defaults", lists only the first four; the fast-checkout, price-quote and pre-order handlers were added later.

Gotchas

  • Calling it again registers every default a second time (the registry never de-duplicates), duplicating GTM, the bundles script and the checkout script.

  • hookRegistry.clear(HookName.BODY_START) or BODY_END, and unmounting any useHook on those names, removes these defaults along with everything else there.

  • <TwilightProvider gtm={false}> (packages/theme-engine/docs/gtm-integration.md, "Disable Automatic GTM") does not stop the GTM container: the prop is declared but nothing reads it. There is no way to remove only the GTM default; clearing body:start also removes the Sift and store-closed handlers.

  • keys.sift, opening_hours and features are marked "TODO: add to API response" in the StoreSettings type, so those defaults may never render with real store data. This demo store has no opening_hours today.

Related

Source and docs