# Add content to a slot (https://docs.salla.dev/twilight/react/recipes/add-content-to-a-slot)



<Callout title="Framework: TanStack Start">
  Registration below lives in `app/router.tsx`, which is specific to TanStack Start. [See what depends on the framework](https://docs.salla.dev/twilight/react/start/frameworks).
</Callout>

Use this when you want a banner, badge or widget at a fixed point on a page and do not want to fork the page.

**Goal:** show a "free returns" note under the add-to-cart form on the product page.

Engine pages render `<HookSlot name="..." />` at fixed points. You register a handler for a slot name in `hookRegistry`. The slot calls every handler, highest priority first, and renders what they return. Each handler gets a context with the theme data (`twilight`) plus whatever the page passes in. More: [Hooks and slots](https://docs.salla.dev/twilight/react/guide/hooks-and-slots), [HookSlot](https://docs.salla.dev/twilight/react/hooks/hook-slot), [useHook](https://docs.salla.dev/twilight/react/hooks/use-hook).

### Register a handler [#register-a-handler]

```tsx title="app/hooks/index.tsx"
import { hookRegistry, HookName, type HookContext } from '@salla.sa/twilight-theme-engine/hooks';
import type { Product } from '@salla.sa/twilight-theme-engine/types';

export function registerThemeHooks() {
  hookRegistry.register(
    HookName.PRODUCT_DESCRIPTION_END,
    (context: HookContext) => {
      const { product } = context as { product?: Product };
      if (!product) return null; // guard in case the slot is rendered without one
      return <p className="returns-note">Free returns within 14 days</p>;
    },
    50 // priority: higher runs first, default 50
  );
}
```

### Call it once at module scope [#call-it-once-at-module-scope]

```tsx title="app/router.tsx"
import { registerThemeHooks } from './hooks';

registerThemeHooks();
```

## Check it worked [#check-it-worked]

Open a product page: the note appears under the add-to-cart form. Open the same page in the other language and it is still there (translate the text with `t()` in a real theme).

## Slot names [#slot-names]

These are rendered by the engine's pages and layout:

| Area       | Slots                                                                                                                                                                                                                                      |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Page shell | `body:start`, `body:end`, `header:start`, `header:end`, `footer:start`, `footer:end`                                                                                                                                                       |
| Home       | `home:start`, `home:content`, `home:end`                                                                                                                                                                                                   |
| Product    | `product:start`, `product:single.description.start`, `product:single.description`, `product:single.description.end`, `product:single.form.start`, `product:single.form.end`, `product:related.start`, `product:related.end`, `product:end` |
| Listings   | `product:list.start`, `product:list.items.start`, `product:list.items.end`, `product:list.end`                                                                                                                                             |
| Cart       | `cart:start`, `cart:items.start`, `cart:items.end`, `cart:summary.start`, `cart:summary.end`, `cart:end`                                                                                                                                   |

Blog, brands, thank-you and not-found pages have slots too, such as `blog:tag.start`, `brands:index.items.start`, `thank-you:start` and `notfound:start`.

<Callout type="warn" title="Traps">
  * **Not every `HookName` entry is rendered.** `HOMEPAGE_SLIDER`, `HOMEPAGE_BANNERS` and `HOMEPAGE_FEATURED` (`homepage:*`) have no slot in the engine, so a handler there never shows. Use `home:start`, `home:content` or `home:end`.
  * **A forked page only has the slots you re-render.** If you [fork a route](https://docs.salla.dev/twilight/react/recipes/fork-a-built-in-route), keep its `HookSlot`s.
  * **A handler that throws renders nothing.** The error is swallowed (logged in development only).
  * **Return `null` when there is nothing to show.** The slot renders whatever you return.
  * **Never register inside a component.** It adds a duplicate handler on every mount.
  * **The context type is loose.** Cast it and guard for missing values.
</Callout>

## Next [#next]

* [Fork a built-in route](https://docs.salla.dev/twilight/react/recipes/fork-a-built-in-route) when a slot is not enough.
* [Hooks and slots](https://docs.salla.dev/twilight/react/guide/hooks-and-slots) for the full model.
