Home page blocks
How a block declared in twilight.json and arranged by the merchant becomes a registry key and then a rendered section.
A store's home page is not a fixed page: the merchant builds it from blocks (a banner, a product slider, a brands grid) and chooses their order. Your theme supplies the components that draw them.
Three names have to line up: the path in twilight.json, the path the API sends, and the key your theme registers. Step through the journey of one block.
1. The theme declares its blocks
A theme's own home blocks (a brands grid, a special slider) are listed in twilight.json under components. Each has a path such as home.brands, a title in Arabic and English, and the fields a merchant fills in.
In engine terms
components[] entries: { key, path: 'home.<name>', title: { ar, en }, icon, image, fields[] }; fields use the same type/format vocabulary as settings. The engine's built-in blocks are not declared there: the reference theme lists them in features instead (component-products-slider, component-featured-products…).
The names that must line up
| twilight.json path | API path | After homeLoader | Registry keys tried | Key you register |
|---|---|---|---|---|
home.brands | home.brands | brands | home:brands | brands |
| not declared (built in) | fixed-banner | fixed-banner | home:fixed-banner | fixed-banner, in DefaultHomeComponents |
| not declared (built in) | featured-products, view_style: style2 | featured-products | home:featured-products:style2, then home:featured-products | featured-products:style2, in DefaultHomeComponents |
import { createRouter } from '@salla.sa/twilight-theme-engine/tanstack';
import {
registerHomeComponents,
DefaultHomeComponents,
} from '@salla.sa/twilight-theme-engine/routes/home';
import { Brands } from './components/home/Brands';
import { routeTree } from './routeTree.gen';
// Keep the spread: nothing is registered automatically.
// twilight.json "home.brands" → API path "home.brands" → loader "brands" → key "home:brands"
registerHomeComponents({ ...DefaultHomeComponents, brands: Brands });
export function getRouter() {
return createRouter(routeTree);
}
import { memo } from 'react';
import type { HomeComponentsProps } from '@salla.sa/twilight-theme-engine/routes/home';
import type { Brand } from '@salla.sa/twilight-theme-engine/types';
import { Link } from '@salla.sa/twilight-theme-engine/components/common';
export const Brands = memo(function Brands({ data }: HomeComponentsProps) {
// The merchant's field values are on data, next to path and position.
const brands = (data.brands ?? []) as Brand[];
if (brands.length === 0) return null;
return (
<div className="container">
{typeof data.title === 'string' && <h2>{data.title}</h2>}
{brands.map((brand) => (
<Link key={brand.id} to={brand.url}>
<img src={brand.logo} alt={brand.name} width={160} height={120} loading="lazy" />
</Link>
))}
</div>
);
});
This store's blocks, as the API sends them
Loading the home page blocks…
import { useQuery } from '@tanstack/react-query';
import { home } from '@salla.sa/twilight-theme-engine/api/home';
export function HomeOutline() {
const { data = [] } = useQuery(home.queries.components());
return (
<ol>
{data.map((block, index) => (
// Some paths start with "home.": registered components use the name after it.
<li key={index}>{block.path.replace('home.', '')}</li>
))}
</ol>
);
}
Why it matters
- Every built-in block disappeared after you registered your own: the
...DefaultHomeComponentsspread is missing. Registration never happens automatically. - A yellow "Unknown component: brands" card in development: the block reached the page but nothing is registered under
home:brands. Check the key you pass toregisterHomeComponents(nohome.and nohome:in it). - The merchant cannot add your block in the dashboard: it is registered in code but not declared in
twilight.jsoncomponents. - A block below the fold is empty in the page source: blocks after the first three mount only when scrolled into view, so do not rely on them for SEO.
- One broken block does not break the page: it renders nothing in production. Check the development error card for the stack.
Go deeper: registerHomeComponents, DefaultHomeComponents, HomeComponentRenderer, registerHomeComponentConfig, the home api, RenderWhenVisible and the Home route module. The registry itself is explained in Overriding an engine component.