Shared types

The small objects that the other data pages reuse, such as Money, Category, Brand, MenuItem, Language and paginated results.

Use this page when a field on the store, product or cart points to a smaller shape (an image, a category, a price object) and you need its fields.

All types import from @salla.sa/twilight-theme-engine/types, except where a section says otherwise:

import type { Money, Category, MenuItem } from '@salla.sa/twilight-theme-engine/types';

The examples marked "Public store API" are real answers from the public demo store.

Money

Where it appears: product variants (skus[].price), base_currency_price on a product, and cart lines (price, total and more).

Example (public store API, a product's base_currency_price):

{ "currency": "SAR", "amount": 3 }
FieldTypeDescription
amountnumberThe amount as a number.
currencystringCurrency code.
formatted?stringReady-made text, when the API sends one.

Many price fields are plain numbers instead: on a product, price, regular_price and sale_price are numbers (199), and currency is a separate field. Cart lines can be either. Turn both into a number first, then format it with useMoney:

import type { Money } from '@salla.sa/twilight-theme-engine/types';
import { useMoney } from '@salla.sa/twilight-theme-engine/hooks/useMoney';

function Price({ value }: { value: number | Money }) {
  const { format } = useMoney();
  const amount = typeof value === 'number' ? value : value.amount;
  return <>{format(amount, { currency: typeof value === 'number' ? undefined : value.currency })}</>;
}

Images

Where it appears: Product.image and Product.images.

Example (public store API):

{ "url": "https://cdn.salla.sa/mQgZlG/c58e19d6-...-500x500.jpg", "alt": "Chair" }
FieldTypeDescription
id?numberImage id.
url?stringImage URL.
alt?stringAlt text; usually the product name.
video_url?stringVideo URL when the item is a video, otherwise null.
video_type?"video" | "youtube" | nullyoutube for a YouTube link, video for an uploaded file, null for an image.
type?"image" | "video"Media kind. The API also sends '3d-image' for a 3D main image.
three_d_image_url?string.glb 3D model URL, or "" when none.
main?booleanTrue for the main image.
sort?numberPosition in the gallery.

images can be null when a product has a single image: read image first, and use images ?? []. Images on categories, brands, cart lines and menu items are plain URL strings, not this object.

Category

Where it appears: category lists and pages (category API), and on a product as a smaller ProductCategory (name, url, optional id, icon, image).

Example (public store API, GET categories):

Category
{ 7 }
:"QlayPG"short string id
:1309167683numeric id
:"Accessories"
:"https://raed.salla.design/en/accessories/c1309167683"
:"sicon-store"
:"https://cdn.salla.sa/mQgZlG/uoE6TbBZZQxS8Dlp8Ca7exuQGZupLnK9AOpRsUGt.jpg"
:[ 0 ]
Click a key to copy its path.
FieldTypeDescription
idstring | numberId as the API sends it, a hashed string such as "xzjvNQ".
id_?numberThe raw numeric id, e.g. 868669273.
namestringCategory name.
description?stringCategory description.
urlstringFull URL of the category page.
icon?string | nullCSS icon class, e.g. "sicon-store", or null.
image?string | nullFlat image URL string (NOT a nested ProductImage object), or null. Aligns with both the API response and Twig.
products_count?numberNumber of products in the category.
sub_categories?Category[]Child categories, possibly empty.
items?Category[] | nullMirror of sub_categories on parent categories (an array, possibly empty); null on some root categories without children.

Brand

Where it appears: the brands list, brand pages and the home brands block. The engine has two Brand types:

  • Brand from @salla.sa/twilight-theme-engine/types: id: number, name, logo, url. This is the small card shape.
  • Brand from @salla.sa/twilight-theme-engine/routes: the full record used by the brands pages and the brand API.

Example (public store API, GET brands, description shortened):

Brand
{ 11 }
:1146128145
:"Bassett"
:"Bassett"
:"..."
:"https://cdn.salla.sa/mQgZlG/JsX391vNFdRaHewpcjjDp6qs2eUHC9uObr4RXeIf.jpg"
:"https://cdn.salla.sa/mQgZlG/hz0de0ALihzWnLGMUatUE9UvdLMW8kj4YPO2ddNL.jpg"
:true
:"ب"first letter, Arabic
:"B"first letter, Latin
:[ 0 ]
:{ 3 }
:"Bassett"
:""
:""
Click a key to copy its path.
FieldTypeDescription
idstring | numberBrand id. Numeric on the Store API (brands and brands/{id}); string in older fixtures.
urlstringBrand page URL.
namestringBrand name.
labelstringDisplay label.
descriptionstringBrand description.
banner?string | nullBanner image URL, or null.
logostringLogo image URL. Always present; required to match Twig template usage.
statusbooleanWhether the brand is active.
ar_charstringFirst Arabic letter, used to group the brands directory.
en_charstringFirst Latin letter, used to group the brands directory.
channels?string[]Sales channels the brand is available on.
metadata?{ title: string; description: string; url: string; }SEO data.

metadata

FieldTypeDescription
titlestringSEO title.
descriptionstringSEO description.
urlstringSEO URL.

id is typed string | number and the API sends a number. Compare with String(brand.id).

Where it appears: the header and footer menus (menu API), usually passed to your header and footer components.

Example (public store API, GET menus/header, one item with one child):

Menu item
{ 7 }
:1309167683
:4
:"Accessories"
:"https://raed.salla.design/en/accessories/c1309167683"
:"https://cdn.salla.sa/mQgZlG/uoE6TbBZZQxS8Dlp8Ca7exuQGZupLnK9AOpRsUGt.jpg"
:true
:[ 1 ]
:{ 5 }
Click a key to copy its path.
FieldTypeDescription
idstring | numberItem id. Built-in links use a word, such as "offers" or "blog".
titlestringText to show.
urlstringLink URL.
image?string | nullRoot-level image the merchant attached in the menu builder, when there is one.
children?MenuItem[]Sub-items. Missing on some items: use children ?? [].
has_children?booleanWhether there are sub-items. Missing on some items.
target?stringLink target, e.g. "_self".
order?numberSort position.
attrs?stringRaw HTML attribute string for the item element (e.g. id="offers").
link_attrs?stringRaw HTML attribute string for the anchor (e.g. target="_self" class="offers-link").

Where it appears: useBreadcrumbs(page) returns a list of them for the trail above a page.

Example (illustrative values):

[
  { "name": "Home", "url": "/" },
  { "name": "Accessories", "url": "/accessories/c1309167683" }
]
FieldTypeDescription
namestringLabel.
urlstringLink target.
import { useBreadcrumbs } from '@salla.sa/twilight-theme-engine/hooks';

const crumbs = useBreadcrumbs(page); // [] on the home page

If the page has no trail of its own, the hook returns Home plus the page title. On a product page it inserts the list the customer came from.

Page

Where it appears: every route loader returns a page object, which components pass to useBreadcrumbs and use for the title.

Example (illustrative values):

{ "slug": "product.single", "title": "Chair", "id": 677709975, "url": "/chair/p677709975" }
FieldTypeDescription
titlestringPage title.
slugstringRoute id of the page, such as "product.single".
description?stringPage description.
id?string | numberEntity ID associated with the page (e.g. category ID on a category page).
url?stringURL of the current page.
parent?{ id: string | number; name: string; url: string; } | nullParent entity, if any.
breadcrumbs?Breadcrumb[]Breadcrumb trail built by the route loader. If omitted, useBreadcrumbs provides a fallback.

parent

FieldTypeDescription
idstring | numberParent entity id.
namestringParent entity name.
urlstringParent entity URL.
FieldTypeDescription
namestringLabel.
urlstringLink target.

A merchant's custom page is a Page plus content (HTML), metadata and created_at, called StaticPage. The page API returns the raw record, for example from the public store: { "id": 153787377, "name": "Our team", "content": "<p>...</p>", "type": "general" }.

Language

Where it appears: useTwilight().settings.languages, the list behind a language switcher.

Example (public store API, languages.EN, plus the iso_code the engine adds):

Language
{ 6 }
:"English"
:"en"
:"en"
:"https://assets.salla.sa/images/flags/en.svg"
:false
:"gb"
Click a key to copy its path.
FieldTypeDescription
namestringLanguage name written in that language.
codestringLanguage code.
iso_code?stringSame as code — the SDK's salla-localization-modal reads iso_code, not code.
urlstringFlag icon URL (e.g., "https://assets.salla.sa/images/flags/ar.svg") - NOT the store URL.
is_rtlbooleanWhether the language is written right to left.
country_codestringCountry code used to pick the flag.
has_custom_translations?booleanWhether this store has its own wording for this language. Written by Languages-Module when a merchant saves a translation, so a theme can tell without fetching whether translations/<storeId>/<iso>.json is worth asking for — most stores have none, and a miss is an uncached round trip on every render. Optional because it is new: a store nobody has written it for carries no key at all, and absent has to read as false. Do not treat a missing flag as "ask anyway".

For the current language, use useTranslation() (locale, direction).

Pagination

Where it appears: lists such as product.list return PaginatedResult<T> from @salla.sa/twilight-theme-engine/api/types.

interface PaginatedResult<T> {
  items: T[];
  next: string | null; // null on the last page
}

Example (illustrative values):

{ "items": [ /* Product, Product, ... */ ], "next": "<opaque cursor>" }
FieldTypeDescription
itemsT[]The items of this page.
nextstring | nullCursor for the next page, null on the last. Pass it back as cursor; do not parse it.

The raw API also sends a cursor object (the Pagination type). Product list results keep it as cursor next to items; cursor.current is the page number for display only.

FieldTypeDescription
next?string | nullCursor for the next page, or null on the last page.
previous?string | nullCursor for the previous page, or null on the first page.
currentnumberCurrent page number, for UI display only.
const first = await product.list({ source: 'latest' });
const second = first.next ? await product.list({ source: 'latest', cursor: first.next }) : null;

Testimonial and Slider

The types exist for theme code that renders store testimonials and home sliders. The engine declares them but does not load them from a named API module, so fill them from the data your own loader or the home block gives you.

Testimonial

FieldTypeDescription
idnumberTestimonial id.
namestringName of the customer who wrote it.
contentstringTestimonial text.
rating?numberStar rating the customer gave.
image?stringCustomer image URL.

Slider

FieldTypeDescription
idnumberSlide id.
imagestringSlide image URL.
link?stringURL the slide links to.
title?stringSlide title.
description?stringSlide text.

Next

On this page