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

menu

objectBeginnerserverbrowserlive demo

Fetches the header and footer menus the merchant built in the Salla dashboard, in the language of the current page.

import { menu } from '@salla.sa/twilight-theme-engine/api/menu';

In plain words

Merchants build their store's navigation in the Salla dashboard: which categories and pages appear in the header, which links go in the footer. menu.header() and menu.footer() return those lists, each item with a title, a url and, for dropdowns, children.

They always answer in the language of the page being rendered; you never pass a language.

Signature

menu.header(): Promise<MenuItem[]>    // GET menus/header?lang=<page locale>
menu.footer(): Promise<MenuItem[]>    // GET menus/footer?lang=<page locale>
menu.queries.header()                 // key ['menus', 'header', locale], staleTime 5 min
// there is no menu.queries.footer()

interface MenuItem {
  id: number | string;
  title: string;
  url: string;
  children?: MenuItem[];
  has_children?: boolean;
}

Try it live

The menus the merchant built in the Salla dashboard, in the page's language. Categories nest under children.Try this: switch to footer and look at the ids: some are numbers, some are paths, one is missing. Then flip the language pill.
Storefront canvas · en · LTR

Loading the header menu…

Controls
What a theme writes
import { useQuery } from '@tanstack/react-query';
import { menu } from '@salla.sa/twilight-theme-engine/api/menu';
import { Link } from '@salla.sa/twilight-theme-engine/components/common';

export function MainNav() {
  const { data = [] } = useQuery(menu.queries.header());

  return (
    <ul className="main-nav">
      {data.map((item) => (
        <li key={item.id}>
          <Link to={item.url}>{item.title}</Link>
        </li>
      ))}
    </ul>
  );
}

Example

app/components/layout/MainNav.tsx
import { useQuery } from '@tanstack/react-query';
import { menu } from '@salla.sa/twilight-theme-engine/api/menu';
import { Link } from '@salla.sa/twilight-theme-engine/components/common';

export function MainNav() {
  const { data = [] } = useQuery(menu.queries.header());

  return (
    <ul className="main-nav">
      {data.map((item) => (
        <li key={item.id ?? item.url}>
          <Link to={item.url}>{item.title}</Link>
        </li>
      ))}
    </ul>
  );
}

How it behaves

  • The language is getTwilightContext().locale (set by the root loader), sent as ?lang=. menu.queries.header() reads it when called, so build the options during render, not once at module level.

  • Both functions return data || [], so a missing menu is an empty array. Both endpoints are public.

  • The engine MainMenu and MobileMenu read menu.queries.header(). The engine footer does not use menu.footer(): it renders Salla's own menu web component.

  • Live items carry more than the type declares: target, order, image, and attrs / link_attrs as raw HTML attribute strings (demo store).

Gotchas

  • There is no menu.queries.footer(). Build the options yourself and key them on the locale (['menus', 'footer', locale]); a key without the locale can hand the Arabic footer to the English page from the cache.

  • id is not a safe React key on its own: header items mix numbers (category ids) and words ("offers"), footer page links use paths ("about-us/page-1061977328"), and a footer item can have no id at all (the blog link on the demo store). Fall back to url.

Related

Source and docs