Header and footer

The Header, Footer, PageAdvertisements, Copyright and WidgetHead pieces of the master layout, and the slots you can fill.

MasterLayout is built from smaller pieces. You can place them yourself in a custom layout, or leave them alone and fill their slots.

import {
  Header,
  Footer,
  Copyright,
  WidgetHead,
  PageAdvertisements,
} from '@salla.sa/twilight-theme-engine/components/layout';

Header, Footer, Copyright and WidgetHead load lazily. PageAdvertisements does not, so it renders on the server without a <Suspense> boundary.

PieceWhat it is
HeaderThe store header: the merchant's announcement bars, logo, menu, search, cart and account.
FooterThe footer: logo and description, the store phone and email, social links, trust badges, the footer menu, app links, the copyright line and payment methods. Optional inSheet prop.
PageAdvertisementsThe merchant's announcement bars for the current page. No props.
CopyrightA copyright line on its own, with an optional storeName prop. The footer does not use it.
WidgetHeadDraws nothing. It runs head-level work in the browser and offers the head:start, head and head:end slots. No props.

The engine's Footer is the shared default: the starter theme renders it as is, inside its own layout. Three things change how it looks:

  • Layout: the theme setting footer_layout. 'columns' (the default) puts the brand block, the footer menu and the app links side by side. 'centered' stacks everything in one centered column, with the footer menu as one inline row instead of a titled column.
  • Colours: the theme setting footer_is_dark.
  • Text: the section titles are the translations blocks.footer.important_links and blocks.footer.download_apps.

It shows no contact details (phone, email). Show those your own way if your theme needs them.

Pass inSheet when you render it somewhere other than the page's footer, such as a mobile menu sheet. It then renders a <div> instead of a second <footer>, skips the footer:start and footer:end slots, and skips its footer menu.

app/components/layout/SheetFooter.tsx
import { Suspense } from 'react';
import { Footer } from '@salla.sa/twilight-theme-engine/layout';

export function SheetFooter() {
  return (
    <Suspense fallback={null}>
      <Footer inSheet />
    </Suspense>
  );
}

To change only the copyright line, register a handler for the copyright slot. It receives { storeName }, and any handler replaces the default line ("All rights reserved", the store name, the year):

app/hooks/index.tsx
import { hookRegistry } from '@salla.sa/twilight-theme-engine/hooks';

hookRegistry.register<{ storeName: string }>('copyright', ({ storeName }) => (
  <>© {new Date().getFullYear()} {storeName}. All rights reserved.</>
));

The announcement bar

Merchants add announcement bars in their dashboard ("free shipping this week") and choose the pages each one shows on. PageAdvertisements draws the bars that belong to the current page. The engine Header already renders it; render it yourself only in a header of your own.

app/components/layout/Header.tsx
import { PageAdvertisements } from '@salla.sa/twilight-theme-engine/components/layout';

export function Header() {
  return (
    <header>
      <PageAdvertisements />
      {/* the rest of your header */}
    </header>
  );
}
  • The ads load with the store settings, before the page renders, so the bar is in the server HTML and does not push the page down when it appears. If that request fails or takes more than 3 seconds, the bar loads in the browser instead.
  • A bar shows when its pages list holds all or the current page's slug. It follows client-side navigation.
  • A visitor who closes a bar does not see it again in that browser. Inside the theme editor every bar shows, closed or not, and inside the Salla mobile app none do.
  • It uses the same classes as Salla's advertisement web component (.s-advertisement, .s-advertisement-content, .s-advertisement-action), so you can restyle it with those.

Add content without replacing anything

The header and footer expose these slots:

SlotPosition
header:startBefore the header content
header:endAfter the header content
footer:startBefore the footer content
copyrightThe copyright line in the footer's bottom row
footer:endAfter the footer content

See Add content to a slot for the steps. Slots keep your changes small and your theme easy to update.

Write your own components and use them inside your own layout:

app/components/layout/ThemeLayout.tsx
import type { LayoutProps } from '@salla.sa/twilight-theme-engine/types';
import { Header } from './Header';
import { Footer } from './Footer';

export function ThemeLayout({ children }: LayoutProps) {
  return (
    <div className="app-inner flex min-h-full flex-col">
      <Header />
      <main id="main-content" className="flex-1">
        {children}
      </main>
      <Footer />
    </div>
  );
}

To replace only the header, import Footer from @salla.sa/twilight-theme-engine/layout instead of ./Footer and wrap it in <Suspense>, as the starter theme does.

Then pass the layout to the provider. See MasterLayout for the complete steps and what you must keep.

See also

On this page