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

Home page blocks

Beginner8 min

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.

twilight.jsoncomponents[]Merchant arrangesorder + fieldscomponent/listblocks, in orderhomeLoaderstrips 'home.'Engine blocksDefaultHomeComponentsapp/router.tsxregisterHomeComponentsregistryhome:<key>Registry lookuppath:style › pathHomePageone renderer per blockNo matchyellow card in devYour blockprops: { data }Lazy sectionRenderWhenVisible

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 pathAPI pathAfter homeLoaderRegistry keys triedKey you register
home.brandshome.brandsbrandshome:brandsbrands
not declared (built in)fixed-bannerfixed-bannerhome:fixed-bannerfixed-banner, in DefaultHomeComponents
not declared (built in)featured-products, view_style: style2featured-productshome:featured-products:style2, then home:featured-productsfeatured-products:style2, in DefaultHomeComponents
app/router.tsx
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);
}
app/components/home/Brands.tsx
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

The blocks the merchant arranged on the home page, in order, exactly as the API sends them.Try this: open a few blocks: each one carries its own settings at the top level, next to path.
Storefront canvas · en · LTR

Loading the home page blocks…

Controls
What a theme writes
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 ...DefaultHomeComponents spread 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 to registerHomeComponents (no home. and no home: in it).
  • The merchant cannot add your block in the dashboard: it is registered in code but not declared in twilight.json components.
  • 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.
Check yourself

twilight.json declares a block with path: 'home.lookbook'. Which key do you pass to registerHomeComponents?