Store data
Every field of the store object returned by useStore and useTwilight().store, with real example values from a demo store.
Read this page when you need to know what the store object looks like: which fields exist, which can be missing, and where to find things like languages and currencies.
Where you read it
There are two ways to get the same store object.
| You write | You get |
|---|---|
useStore() | The store fields plus a refresh() method. |
useTwilight().store | The store fields only. |
The engine loads the store once per request from the store/settings API and hands it to the provider at the root of your app. It is available in server rendering and in the browser, from the first render.
Languages and currencies are not inside the store object. They come from the same API response and you read them from useTwilight().settings (see Common tasks). The theme object is covered in Theme data.
import { useStore } from '@salla.sa/twilight-theme-engine/hooks/useStore';
export function StoreBadge() {
const { name, logo, url, contacts, settings } = useStore();
return (
<a href={url}>
<img src={logo} alt={name} />
{contacts.email && <span>{contacts.email}</span>}
{settings.rating_enabled && <span>Rated by customers</span>}
</a>
);
}The full object
This is the store of the public demo store 1510890315, shortened: long arrays are cut to a few items and long strings are trimmed. Search it, expand what you need, and click a key to copy its path. Notes sit next to the less obvious fields.
Typed and untyped fields
The Store type describes the fields themes read. The API can return more (for example extra settings.rating options). TypeScript does not know about those, so read them with a cast or your own type.
Fields
The tables below are generated from the engine's Store type. Nested objects get their own table, named by their path (settings.auth, settings.cart, and so on). Example values are in the explorer above.
| Field | Type | Description |
|---|---|---|
id | number | Store id. |
name | string | Store name, as the merchant typed it. |
username | string | Store username. |
description | string | Store description, as HTML rather than plain text. |
slogan? | string | Store slogan. |
logo | string | Logo image URL. |
url | string | Store home URL. On a multilingual store it ends with the language prefix (/ar/). |
api? | string | Base URL of the store API. |
icon | string | Icon image URL. |
favicon? | string | Favicon URL. Marked for removal. |
about? | string | Longer "about the store" text. |
created_at? | string | When the store was created. |
updated_at? | string | When the store was last updated. |
meta? | { title?: string; description?: string; keywords?: string; } | Default SEO values for pages that set none. Empty strings when the merchant set none. |
settings | StoreSettings | Store-level settings (auth, cart, product, ratings and so on). |
contacts | StoreContacts | Contact details the store shows to customers. |
social | StoreSocial | Links to the store's social media profiles. |
country | string | Store's default country code (uppercase ISO, e.g., "SA"). |
store_country? | string | Country the store operates in (uppercase ISO, e.g., "SA"). |
ray? | number | Internal platform request value; nothing in a theme should depend on it. |
features? | string[] | Platform feature flags that are on for this store. |
apps? | StoreApps | Mobile app download links. |
template? | string | null | Reserved by the platform; usually null. |
scope? | StoreScope | null | The selected market or branch, or null when the store has none. |
is_merchant? | boolean | Whether the visitor is the merchant. Marked for removal: rely on theme.mode instead. |
support_pickup? | boolean | Whether the store offers pickup (mirrors shipping.support_pickup). |
shipping? | StoreShipping | Shipping and delivery options the store offers. |
rating? | { total: number; stars: number; } | Store-wide rating summary. |
ratings? | { stars: number; total: number; }[] | Rating distribution: how many ratings gave each star value. |
order_instruction? | StoreOrderInstruction | Order confirmation page messages (HTML strings). |
meta
| Field | Type | Description |
|---|---|---|
title? | string | Default page title. |
description? | string | Default page description. |
keywords? | string | Default page keywords. |
settings
| Field | Type | Description |
|---|---|---|
auth | { email_allowed: boolean; mobile_allowed: boolean; is_email_required: boolean; countrie... | Sign-in and registration options. |
cart | { apply_coupon_enabled: boolean; basket_gap?: boolean; multi_coupon?: boolean; } | Cart options. |
product | { total_sold_enabled: number; manual_quantity?: boolean; fit_type?: 'cover' | 'contain'... | Product page and listing options. |
category | { testimonial_enabled: boolean; } | Category page options. |
payments? | string[] | Enabled payment methods. |
arabic_numbers_enabled | boolean | Show Arabic-Indic digits. |
content_copyright? | boolean | Content copy protection is on. |
use_sar_symbol? | boolean | Show the Saudi riyal symbol instead of the currency code. |
is_multilingual | boolean | The store offers more than one language. |
currencies_enabled | boolean | The store offers more than one currency. |
rating_enabled | boolean | Ratings are turned on for the store. |
rating? | { is_enabled: boolean; show_on_category: boolean; show_on_product: boolean; can_rate_st... | Rating and review options. |
blog? | { is_enabled: boolean; allow_likes_and_comments: boolean; } | Blog options. |
is_loyalty_enabled? | boolean | The loyalty program is on. |
keys? | { maps?: string; gtm?: string[]; sift?: string; } | Public keys for third-party services. |
is_salla_gateway? | boolean | The store uses the Salla payment gateway. |
upload_size? | number | Maximum upload size. |
tax? | { number?: string; certificate?: string | null; taxable_prices_enabled: boolean; } | Tax details. |
certificate? | { id: string; } | null | Commerce certificate details, or null. |
made_in_ksa? | boolean | Show the "Made in KSA" mark. |
commercial_number? | string | null | Commercial registration number, or null. |
freelance_number? | string | null | Freelance license number, or null. |
buy_now? | string | BuyNowSettings | null | Fast-checkout (Apple Pay style) configuration object, or null when off. Typed string | null in older releases; the API sends an object. |
installments? | string | unknown[] | Record<string, unknown> | null | Installment providers. An empty array means none, and an empty array is truthy. |
quick_order? | string | Record<string, unknown> | null | Quick-order configuration, or null when off. |
bullet_delivery? | string | Record<string, unknown> | null | Fast ("bullet") delivery configuration, or null when off. |
ticketing_system_enabled? | boolean | Support tickets are on. |
opening_hours? | OpeningHoursSettings | Opening hours for receiving orders. Not yet sent by the API. |
features? | { price_quote?: boolean; 'pre-order-campaigns'?: boolean; } | Feature flags. Not yet sent by the API. |
settings.auth
| Field | Type | Description |
|---|---|---|
email_allowed | boolean | Customers can sign in with email. |
mobile_allowed | boolean | Customers can sign in with a mobile number. |
is_email_required | boolean | Email is required when registering. |
countries? | string[] | Country codes allowed for mobile numbers, lowercase. |
force_login? | boolean | Visitors must sign in to browse the store. |
whatsapp_allowed? | boolean | Customers can sign in through WhatsApp. |
whatsapp_countries? | string[] | Country codes where WhatsApp sign-in is offered. |
whatsapp_merchant_allowed? | boolean | Whether the merchant has enabled WhatsApp sign-in. |
user_country? | string | Visitor's detected country (uppercase ISO), e.g. "DE". |
config? | unknown | Extra sign-in configuration sent by the platform; its shape is not typed, and it is null when unused. |
settings.cart
| Field | Type | Description |
|---|---|---|
apply_coupon_enabled | boolean | Show the coupon field in the cart. |
basket_gap? | boolean | Platform flag for the cart layout. |
multi_coupon? | boolean | More than one coupon can be applied. |
settings.product
| Field | Type | Description |
|---|---|---|
total_sold_enabled | number | Show how many units were sold. It is 1 or 0, not a boolean. |
manual_quantity? | boolean | Customers can type the quantity instead of using a stepper. |
fit_type? | "contain" | "cover" | null | How product images fill their frame; null when not set. |
related_products_enabled? | boolean | Show related products. |
filters? | boolean | ProductFiltersSettings | null | Switches for the listing filters, one per filter ({ category_id: true, price: true, ... }). |
product-recommendation-type? | string | Kind of product recommendations to show. The key has hyphens, so read it with brackets. |
bought_together? | boolean | Show "bought together" products. |
show_comments? | boolean | Show product comments. |
user_can_comment? | boolean | Customers can comment on products. |
show_price_as_dash? | boolean | Hide prices and show a dash instead. |
notify_options_availability? | boolean | Offer "notify me" for sold-out options. |
show_special_offers? | boolean | Show special offers on the product. |
show_more? | boolean | Show the "show more" control. |
availability_notify? | { email: boolean; sms: boolean; mobile: boolean; whatsapp: boolean; } | null | Channels offered for "notify me when available", or null when the feature is off. |
settings.product.availability_notify
| Field | Type | Description |
|---|---|---|
email | boolean | Notify by email. |
sms | boolean | Notify by SMS. |
mobile | boolean | Notify by mobile notification. |
whatsapp | boolean | Notify by WhatsApp. |
settings.category
| Field | Type | Description |
|---|---|---|
testimonial_enabled | boolean | Show testimonials on category pages. |
settings.rating
| Field | Type | Description |
|---|---|---|
is_enabled | boolean | Ratings are on. |
show_on_category | boolean | Show ratings on category pages. |
show_on_product | boolean | Show ratings on product pages. |
can_rate_store | boolean | Customers can rate the store. |
can_rate_product | boolean | Customers can rate products. |
can_rate_shipping | boolean | Customers can rate shipping. |
show_rating_summary | boolean | Show the rating summary block. |
show_recommendation | boolean | Show the "would you recommend" question. |
allow_likes | boolean | Reviews can be liked. |
allow_attach_images | boolean | Reviews can include images. |
allow_contact_support | boolean | Show a contact-support action on reviews. |
allow_update | boolean | Customers can edit their review. |
update_period | number | Number of days a review can be edited. |
can_customer_hide_name | boolean | Reviewers can hide their name. |
visitor_can_comment | boolean | Visitors who are not signed in can comment. |
page_feedback_enabled | boolean | Page feedback is on. |
settings.blog
| Field | Type | Description |
|---|---|---|
is_enabled | boolean | The blog is on. |
allow_likes_and_comments | boolean | Readers can like and comment on articles. |
settings.keys
| Field | Type | Description |
|---|---|---|
maps? | string | Maps API key for the storefront. |
gtm? | string[] | Google Tag Manager container ids. |
sift? | string | Sift Science beacon key for fraud detection. Not yet sent by the API. |
settings.tax
| Field | Type | Description |
|---|---|---|
number? | string | Tax registration number. |
certificate? | string | null | Tax certificate image URL, or null. |
taxable_prices_enabled | boolean | Prices include tax. |
settings.certificate
| Field | Type | Description |
|---|---|---|
id | string | Commerce certificate id. |
settings.opening_hours
| Field | Type | Description |
|---|---|---|
enabled? | boolean | The store only takes orders during opening hours. |
hours? | Record<string, string[]> | Time ranges per lowercase day name (monday, tuesday, ...). A day with no entry or an empty list is closed. |
settings.features
| Field | Type | Description |
|---|---|---|
price_quote? | boolean | Customers can ask for a price quote. |
pre-order-campaigns? | boolean | Pre-order campaigns are available. |
contacts
| Field | Type | Description |
|---|---|---|
mobile? | string | Mobile number. |
phone? | string | Phone number. |
email? | string | Support email address. |
whatsapp? | string | WhatsApp link or number. |
telegram? | string | Telegram link. |
social
| Field | Type | Description |
|---|---|---|
instagram? | string | Instagram profile link. |
snapchat? | string | Snapchat profile link. |
twitter? | string | X (Twitter) profile link. |
youtube? | string | YouTube channel link. |
facebook? | string | Facebook page link. |
pinterest? | string | Pinterest profile link. |
maroof? | string | Maroof page link. |
whatsapp? | string | WhatsApp link. |
tiktok? | string | TikTok profile link. |
apps
| Field | Type | Description |
|---|---|---|
appstore? | string | App Store link. |
googleplay? | string | Google Play link. |
scope
| Field | Type | Description |
|---|---|---|
id? | number | Scope id. |
name | string | Market or branch name. |
selected? | boolean | This scope is the active one. |
type? | string | Scope type, such as a branch. |
is_open? | boolean | The scope is open. |
display_as | "default" | "inline" | "popup" | How the scope selector is shown. |
languages? | string[] | Available language codes (lowercase, e.g., ["ar", "en"]) |
currencies? | string[] | Available currency codes (e.g., ["SAR", "USD"]) |
countries? | string[] | Available country codes (uppercase ISO, e.g., ["SA", "AE"]) |
always_ask? | boolean | Ask the visitor to choose a scope. |
allocation? | unknown | Allocation details sent by the platform, or null; its shape is not typed. |
shipping
| Field | Type | Description |
|---|---|---|
delivery_location? | unknown | Delivery location details, or null when the store has none. |
express_delivery_promise? | unknown | Express-delivery promise details, or null when the store has none. |
support_pickup? | boolean | Whether the store offers pickup. |
has_bullet_delivery_tag? | boolean | Whether products show the fast-delivery ("bullet") tag. |
rating
| Field | Type | Description |
|---|---|---|
total | number | Number of ratings the store has received. |
stars | number | Average star rating. |
ratings
| Field | Type | Description |
|---|---|---|
stars | number | The star value (for example 5). |
total | number | Number of ratings that gave this star value. |
order_instruction
| Field | Type | Description |
|---|---|---|
thank_title? | string | Heading of the order confirmation page. |
order_instructions_payment_waiting? | string | Message shown while the order waits for payment. |
order_instructions_under_review? | string | Message shown while the order is under review. |
order_instructions_completed? | string | Message shown once the order is completed. |
The API still sends more than the type lists: for example settings.rating also carries video and points options (allow_attach_videos, required_attach_images, rating_store_points). buy_now, installments, quick_order and bullet_delivery can be an object, an array or null, so check before you use them (in the demo, buy_now is an object and installments is []).
settings.features is not the top-level features array of strings. settings.opening_hours is read by useOpeningHours. scope is null unless the store uses markets or branches; the demo store has none, so there is no real example in the explorer.
refresh
Only on useStore(). refresh(): Promise<void> re-reads store from the Salla SDK config and merges it, one level deep, over the current store. It does nothing when the SDK is not loaded yet, and ignores errors.
Common tasks
Is the store multilingual, and which languages?
is_multilingual is on the store. The list of languages is on settings, as an array.
import { useTwilight } from '@salla.sa/twilight-theme-engine';
function Languages() {
const { store, settings, locale } = useTwilight();
if (!store.settings?.is_multilingual) return null;
return (
<ul>
{settings.languages?.map((lang) => (
<li key={lang.code} aria-current={lang.code === locale}>
{lang.name} {lang.is_rtl ? '(RTL)' : ''}
</li>
))}
</ul>
);
}Demo values: { "name": "العربية", "code": "ar", "is_rtl": true, "country_code": "sa", "url": "https://assets.salla.sa/images/flags/ar.svg" }. The url is the flag icon, not a page. The engine also adds iso_code, equal to code. See Languages and RTL.
Which currencies are available?
settings.currencies is an object keyed by currency code. amount is the exchange rate against the store currency (in the demo, USD.amount is 0.267589 and SAR.amount is 1). To format prices, use useMoney.
const { store, settings } = useTwilight();
const codes = store.settings?.currencies_enabled ? Object.keys(settings.currencies ?? {}) : [];
// ["AED", "AUD", "BHD", …, "SAR", "USD"]Do customers sign in by email or mobile?
const { auth } = useStore().settings;
const methods = [auth.email_allowed && 'email', auth.mobile_allowed && 'mobile'].filter(Boolean);
// demo store: ["email"]When force_login is true, send visitors to sign in before they browse.
Is the store open right now?
opening_hours is passed to useOpeningHours, which returns isOpen, isEnabled, nextOpen and nextOpenFormatted. It treats the store as open when opening hours are not enabled.
import { useStore } from '@salla.sa/twilight-theme-engine/hooks/useStore';
import { useOpeningHours } from '@salla.sa/twilight-theme-engine/hooks';
function ClosedNotice() {
const { settings } = useStore();
const { isOpen, nextOpenFormatted } = useOpeningHours(settings.opening_hours);
return isOpen ? null : <p>We are closed. We open {nextOpenFormatted}.</p>;
}Is the store in maintenance?
When the merchant closes the store from the dashboard (or the platform closes it, or one market is closed), the settings response carries maintenance: true and maintenance_details: the merchant's title and message (plain text), and button_* fields that are null for customers.
You do not have to handle it. The engine decides per request who sees what, the way the Twig storefront does: a customer gets the maintenance page (MaintenancePage from @salla.sa/twilight-theme-engine/store) in place of your layout, with a 503 status, and nothing else renders, not even the Salla SDK. The merchant gets the store as usual, with the SDK's maintenance bar on top.
const { settings } = useTwilight();
settings.maintenance; // true while the store is closed, for the merchant too
settings.maintenance_details?.title; // what the merchant wroteA store that has not launched yet
Until the merchant finishes launching the store, the settings say maintenance: true too, but maintenance_details then holds the merchant's own "finish launching" notice, which the API sends to every visitor. The engine marks these settings launched: false, and MaintenancePage shows customers the platform's launching-soon page instead: the store's logo, "The store is launching soon" in the platform's words, and the store's contacts. It never shows them that notice.
settings.launched; // false while the store has not launched yet; absent otherwiseHow the engine tells the merchant from a customer
The decision is made once per request, in the root route's beforeLoad, from the settings and the request alone:
- The dashboard's "view store" button (and the partners portal) opens the store with
?utm_source=dashboard&identifier=<store id>(portalfor the portal). Whenidentifieris this store'sstore.id, the request is the merchant's. The engine answers with the store and sets a session cookie,salla_view_as=<store id>, so the browser is remembered as the merchant until it closes, as the Twig storefront's PHP session was. - A request carrying that cookie for this store is the merchant's too. The cookie opens only the store it was set for.
- A session the backend marks as a theme preview (
theme.mode === 'preview') always sees the store. - Anything served from the preview host (
preview.salla.design) always sees the store, closed or not launched yet. The dashboard's theme editor frames your theme there, and a frame on another site carries neither the link from step 1 nor the cookie from step 2, so the host is what says it is a preview. It holds on every page of the preview, not only the first, as the Twig storefront's own preview hosts do. A merchant's domain,salla.saand a dev server are not the preview host, so they are decided by the other steps. On the preview host the engine also initialises the Salla SDK withmaintenance: false, so the SDK's merchant maintenance bar is not drawn over the page being edited. Only the SDK's copy changes: the settings you read withuseTwilight()stay as the API sent them,maintenance: trueincluded. - Everyone else gets the maintenance page and a
503, so search engines keep the store indexed as temporarily unavailable.
While you develop
Your dev server renders against a real store, so a closed store shows you the maintenance page too. In development the engine pins a bar to the top of it with a link, "View the store as the merchant": the dashboard's link from step 1, for this store. Click it once, and the rest of the browser session renders the store. A private window gives you the customer's page back, which is how you check your own maintenancePage below. The bar exists only on the dev server; production builds drop it.
Your own maintenance page
To show your own page instead of the engine's, pass it to the provider beside layout. It takes no props and renders alone: no layout, no route, no SDK. It renders for a store that has not launched yet too, so check settings.launched before you show maintenance_details.
import { TwilightProvider, useTwilight } from '@salla.sa/twilight-theme-engine';
function ClosedPage() {
const { store, settings } = useTwilight();
return (
<main>
<img src={store.logo} alt={store.name} />
{settings.launched === false ? (
<h1>Launching soon</h1>
) : (
<>
<h1>{settings.maintenance_details?.title}</h1>
<p>{settings.maintenance_details?.message}</p>
</>
)}
</main>
);
}
<TwilightProvider translations={themeTranslations} layout={MainLayout} maintenancePage={ClosedPage}>
<Outlet />
</TwilightProvider>;Which payment methods and features are on?
const { settings, features = [] } = useStore();
const acceptsCash = settings.payments?.includes('cod');
const guestCheckout = features.includes('guest-checkout');Free shipping and cart settings
Free shipping progress is part of the cart, not the store. See Cart data. Store-level cart switches are in settings.cart, for example settings.cart.apply_coupon_enabled.
Gotchas
- Many fields are optional.
slogan,about,favicon,api,scope,opening_hours,social.*andcontacts.*can be missing. Use optional chaining and test for presence before you render a link. - Types are looser than the data. Several settings (
filters,buy_now,installments) are unions of a boolean or string, an object andnull, so narrow them before use. Some real fields are not typed at all. store.idis a number, but ids on other objects, such ascertificate.id, are strings. Compare with the right type, or convert before you compare.- Some setting keys are not valid identifiers.
settings.product['product-recommendation-type']andsettings.features['pre-order-campaigns']need bracket access. - Flags that are numbers.
settings.product.total_sold_enabledis1or0. - Language-dependent values. The API response follows the page language, so
urlcarries the language prefix (/en/).nameanddescriptionare what the merchant typed and are not translated. descriptionis HTML. Sanitise it or render it with care.- Empty store object. If the provider is created without settings,
storeis{}until settings arrive, sostore.settingsisundefined. Usestore.settings?.…in code that can run that early. - Server and browser see the same store. It comes from the settings loaded for the request, so server and first browser render match.
refresh()only exists in the browser, because it needs the Salla SDK. useStore()returns a new object on every render. UseuseTwilight().store, which keeps its identity untilrefresh(), or depend on the fields you read, in effect and memo dependencies.settingsonuseTwilight()is notstore.settings.useTwilight().settingsis the whole settings response (store, theme,languages,currencies, and more).store.settingsis only the store-level settings group.
Next
useStoreis the hook reference.- Theme data describes the theme object next to the store.
- Product data and Cart data describe the other objects your components read.