hookRegistry
The one shared list of slot handlers: register content under a slot name, read or clear it, or register many at once with defineHooks.
import { hookRegistry, defineHooks, HookRegistry } from '@salla.sa/twilight-theme-engine/hooks/HookRegistry';In plain words
Think of hookRegistry as a notice board. hookRegistry.register('footer:start', () => <p>Hello</p>) pins a small function under a name, and every <HookSlot name="footer:start" /> shows what that function returns. Higher priority numbers come first; the default is 50.
defineHooks({...}) pins several at once from an object whose keys are slot names. HookRegistry is the class the shared board was made from; a theme uses the shared hookRegistry.
Signature
const hookRegistry: HookRegistry;
class HookRegistry {
register<T = Record<string, unknown>>(name: HookName | string, handler: HookHandler<T>, priority?: number): void; // priority 50
getHandlers(name: HookName | string): HookDefinition[]; // highest priority first
has(name: HookName | string): boolean;
list(): string[];
clear(name: HookName | string): void; // removes every handler under the name
clearAll(): void;
subscribe(name: HookName | string, callback: () => void): () => void;
unsubscribe(name: HookName | string, callback: () => void): void;
}
function defineHooks(hooks: HookHandlers): void;Try it live
import { hookRegistry, HookName } from '@salla.sa/twilight-theme-engine/hooks';
// app/hooks/index.tsx: register once, when the theme starts.
hookRegistry.register(
HookName.HEADER_START,
() => <p className="announcement">{'Free delivery over 200 SAR'}</p>,
);
Example
import { defineHooks, hookRegistry } from '@salla.sa/twilight-theme-engine/hooks/HookRegistry';
import { HookName } from '@salla.sa/twilight-theme-engine/types/hooks';
import { AnnouncementBar } from '../components/AnnouncementBar';
import { ChatWidget } from '../components/ChatWidget';
let registered = false;
export function registerThemeHooks() {
if (registered) return; // register() never de-duplicates
registered = true;
hookRegistry.register(HookName.HEADER_START, () => <AnnouncementBar />, 60);
defineHooks({
[HookName.FOOTER_END]: () => <small>Delivery across the Gulf</small>,
[HookName.BODY_END]: [{ handler: () => <ChatWidget />, priority: 10 }],
});
}
How it behaves
registerappends and re-sorts by priority, highest first, then notifies the slots showing that name.clear(name)andclearAll()notify them too.There is no way to remove one handler: only
clear(name)(everything under the name) orclearAll().The registry lives at module scope: one per browser tab, and one per server process. Registering at module load, as the reference theme does from
app/router.tsx, applies to every request and renders in the server HTML.defineHooksloops overregister. A plain function is registered at priority 50; an array item uses its ownpriority, or 50.The engine's own defaults are in this registry too (see
registerDefaultHooks).Also exported from
@salla.sa/twilight-theme-engine/hooks; both paths share one instance.
Gotchas
registernever de-duplicates: calling the same registration twice renders the content twice. The reference theme does exactly this:app/hooks/index.tsxcallsregisterThemeHooks()when it is imported, andapp/router.tsximports it and calls it again. Guard with a flag, as in the example.Register at module load, never while rendering or per request. On the server the registry outlives the request, so a registration made during a render piles up for every later visitor.
Registering above a default's priority does not replace it (docs/06-hook-system.md, "Override Defaults", suggests it does): both render. And
clear(HookName.BODY_END)also removes the engine'sTwilightBundlesand fast-checkout script.The JSDoc of
defineHooksshows a single{ handler, priority }object as a value. TypeScript rejects it; in plain JavaScript that object is registered as if it were a function, fails when the slot calls it, and the slot silently renders nothing. Use a function, or an array of{ handler, priority }.A
new HookRegistry()is a separate, private board:HookSlotonly reads the exportedhookRegistry.
Related
A named empty place in the page that renders every handler registered under its name, plus a spot where Salla apps inject content.
HookNameThe predefined slot names as a TypeScript enum, so a typo fails to compile instead of silently filling a slot nobody renders.
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.
HookHandler, HookHandlers, HookDefinitionTypes for a slot handler function, the object defineHooks accepts, and a handler as the registry stores it.