Layout and components
The page frame around every route, and how to swap an engine component for your own.
Read this when you want to change how the header, footer, product card or home page look.
A component is a function that returns markup. The engine ships many (header, footer, product card, home blocks), and your theme can replace the ones you want to change. The sections below go from the biggest swap to the smallest.
The layout wraps every page
The layout is the frame around each route: header on top, footer at the bottom, your page in between.
By default the engine uses its MasterLayout. You can pass your own to TwilightProvider in the root route:
import { TwilightProvider } from '@salla.sa/twilight-theme-engine';
import themeTranslations from 'virtual:twilight/theme-translations';
import { ThemeLayout } from '../components/layout/ThemeLayout';
// inside RootComponent
<TwilightProvider translations={themeTranslations} layout={ThemeLayout}>
<Outlet />
</TwilightProvider>A layout is a component that receives children (the current page). This is the starter theme's, shortened:
import { Suspense, type ReactNode } from 'react';
import { Footer } from '@salla.sa/twilight-theme-engine/layout';
import { Header } from './Header';
export function ThemeLayout({ children }: { children: ReactNode }) {
return (
<>
<div className="app-inner flex flex-col min-h-full">
<Header />
<main id="main-content" className="flex-1">
{children}
</main>
<Suspense fallback={null}>
<Footer />
</Suspense>
</div>
{/* global modals: offer, login, scopes … */}
</>
);
}It mirrors the engine's layout: its own header, the engine's footer, and the same global modals. Start from a copy of the engine's layout and change only what you need. The engine's Footer has two looks, picked by the theme setting footer_layout (see Header and footer).
See the MasterLayout reference.
Only the layout swaps the header and footer. To just add a banner above the header, use a hook slot instead of replacing everything.
Swap the product card with the registry
The component registry is a name-to-component map. The engine looks a few names up at render time, so registering your component under that name replaces the built-in one everywhere.
The product card is the main one (product:card). It needs both calls:
import { registry } from '@salla.sa/twilight-theme-engine';
registry.register('product:card', MyProductCard); // seeds an "original"
registry.override('product:card', MyProductCard); // now it counts as an overrideThe engine card only uses your component when register has run first and override after it, so override alone does nothing.
Step by step: Replace the product card.
The engine looks up only these names: product:card, product:gallery, product:list-page and product:list-skeleton
(every product listing page and its loading skeleton), account:layout-pending (the skeleton shown while
account pages load), and the home:* names. Anything else is not swappable this way.
Swap a home block
The home page is built from blocks that a merchant arranges. Each block has a key, and registerHomeComponents
binds keys to components. The starter theme starts from the engine defaults and replaces some:
import {
registerHomeComponents,
DefaultHomeComponents,
} from '@salla.sa/twilight-theme-engine/routes/home';
registerHomeComponents({
...DefaultHomeComponents,
brands: Brands,
'products-slider': ProductsSlider,
});Keep ...DefaultHomeComponents first, so blocks you did not replace still render. To add a brand new block,
see Add a home block.
Or just wrap the engine component
You do not have to rebuild a component. The starter theme's ProductCard imports the engine card
(import { ProductCard as EngineProductCard } from '@salla.sa/twilight-theme-engine/product') and renders it
with small changes. Wrapping keeps engine fixes flowing in.
Which to choose
| Goal | Use |
|---|---|
| New header or footer | Your own layout |
| Different product card | Registry product:card |
| Different home block | registerHomeComponents |
| Add content in one spot | A hook slot |
| Change a whole page | Replace the route |
Every engine component is documented in the Components tab, starting with MasterLayout.
Next: Hooks and slots, to add content without replacing anything.