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

Store

interfaceBeginnerlive demo

Describes the current store and its store-wide settings: name, logo, address, contacts, social accounts, apps and feature switches.

import { Store, StoreSettings, StoreContacts, StoreSocial, StoreApps, StoreScope, OpeningHoursSettings } from '@salla.sa/twilight-theme-engine/types';

In plain words

Store is the shape of the shop a page is rendered for: its name, logo, web address, contact details and social accounts. It also holds settings, a large object of switches the merchant controls in the Salla dashboard: is the coupon box on, does the store sell in several languages, should numbers show in Arabic digits.

A component gets the store from useStore(). Import the type when you write a helper that takes the store or part of it, so your editor can autocomplete settings.cart.apply_coupon_enabled.

Signature

interface Store {
  id: number;  name: string;  username: string;
  url: string;                          // includes the language, and the username on a shared host
  description: string;                  // HTML
  logo: string;  icon: string;  slogan?: string;  about?: string;
  meta?: { title?: string; description?: string; keywords?: string };
  settings: StoreSettings;
  contacts: StoreContacts;
  social: StoreSocial;
  country: string;  store_country?: string;   // uppercase ISO codes, e.g. 'SA'
  apps?: StoreApps;
  scope?: StoreScope | null;
  features?: string[];
  api?: string;  favicon?: string;  ray?: number;  template?: string | null;
  created_at?: string;  updated_at?: string;  is_merchant?: boolean;
}

interface StoreSettings {
  auth: { email_allowed: boolean; mobile_allowed: boolean; is_email_required: boolean;
          countries?: string[]; force_login?: boolean };
  cart: { apply_coupon_enabled: boolean };
  product: { total_sold_enabled: number; show_price_as_dash?: boolean;
             fit_type?: 'cover' | 'contain' | null; filters?: boolean | null; /* … */ };
  category: { testimonial_enabled: boolean };
  arabic_numbers_enabled: boolean;  is_multilingual: boolean;
  currencies_enabled: boolean;  rating_enabled: boolean;
  rating?: { /* 16 review switches */ };
  blog?: { is_enabled: boolean; allow_likes_and_comments: boolean };
  keys?: { maps?: string; gtm?: string[]; sift?: string };
  tax?: { number?: string; certificate?: string | null; taxable_prices_enabled: boolean };
  buy_now?: string | null;  installments?: string | null;  quick_order?: string | null;
  opening_hours?: OpeningHoursSettings;
  features?: { price_quote?: boolean; 'pre-order-campaigns'?: boolean };
  // …payments, use_sar_symbol, is_loyalty_enabled, upload_size and more
}

interface StoreContacts { mobile?: string; phone?: string; email?: string; whatsapp?: string; telegram?: string }

interface StoreSocial {
  instagram?: string;  snapchat?: string;  twitter?: string;  youtube?: string;  facebook?: string;
  pinterest?: string;  maroof?: string;  whatsapp?: string;  tiktok?: string;
}

interface StoreApps { appstore?: string; googleplay?: string }

interface StoreScope {
  name: string;  display_as: 'popup' | 'inline' | 'default';
  id?: number;  type?: string;  selected?: boolean;  is_open?: boolean;  always_ask?: boolean;
  languages?: string[];  currencies?: string[];  countries?: string[];  allocation?: unknown | null;
}

interface OpeningHoursSettings { enabled?: boolean; hours?: Record<string, string[]> }

Try it live

The store this page was rendered with, field by field, against Store and StoreSettings. ✗ marks a value the type does not allow.Try this: pick store.settings and turn on Only mismatches: buy_now is an object and installments an empty array.
Storefront canvas · en · LTR
store.settings.declaredsent by the demo store
authobjectobject {"email_allowed":true,"mobile_allowed":t
cartobjectobject {"apply_coupon_enabled":true,"basket_gap
productobjectobject {"total_sold_enabled":1,"manual_quantity
categoryobjectobject {"testimonial_enabled":true}
paymentsarray | undefinedarray ["mada","credit_card","bank","apple_pay"
arabic_numbers_enabledbooleanboolean false
content_copyrightboolean | undefinedboolean false
use_sar_symbolboolean | undefinedboolean true
is_multilingualbooleanboolean true
currencies_enabledbooleanboolean true
rating_enabledbooleanboolean true
ratingobject | undefinedobject {"is_enabled":true,"show_on_category":tr
blogobject | undefinedobject {"is_enabled":true,"allow_likes_and_comm
is_loyalty_enabledboolean | undefinedboolean false
keysobject | undefinedobject {"maps":"AIzaSyAZpoNuLbXR-VvGYEP4ZbMD9Fq
is_salla_gatewayboolean | undefinedboolean false
upload_sizenumber | undefinednumber 2000
taxobject | undefinedobject {"number":"123456789","certificate":"htt
certificateobject | null | undefinedobject {"id":"1234567890"}
made_in_ksaboolean | undefinedboolean false
commercial_numberstring | null | undefinednull null
freelance_numberstring | null | undefinednull null
buy_nowstring | null | undefinedobject {"label":"Salla","countryCode":"SA","sup
installmentsstring | null | undefinedarray []
quick_orderstring | null | undefinednull null
bullet_deliverystring | null | undefinednull null
ticketing_system_enabledboolean | undefinedundefined
opening_hoursobject | undefinedundefined
featuresobject | undefinedundefined

Sent but not in the type: nothing

Controls
Only mismatches
What a theme writes
import { useStore } from '@salla.sa/twilight-theme-engine/hooks/useStore';

export function InstallmentsNote() {
  const { settings } = useStore();
  // Typed string | null, but the API can send an array, and [] is truthy.
  const installments: unknown = settings.installments;
  const offered = Array.isArray(installments) ? installments.length > 0 : Boolean(installments);
  return offered ? <p className="installments-note">Split your payment</p> : null;
}

Example

app/components/layout/SocialLinks.tsx
import type { StoreSocial } from '@salla.sa/twilight-theme-engine/types';
import { useStore } from '@salla.sa/twilight-theme-engine/hooks/useStore';

const NETWORKS: (keyof StoreSocial)[] = ['instagram', 'twitter', 'tiktok', 'youtube', 'snapchat'];

export function SocialLinks() {
  const { social } = useStore();
  const links = NETWORKS.filter((network) => social[network]);

  return (
    <ul className="social-links">
      {links.map((network) => (
        <li key={network}>
          <a href={social[network]} target="_blank" rel="noopener noreferrer">
            {network}
          </a>
        </li>
      ))}
    </ul>
  );
}

How it behaves

  • Read it with useStore() (the fields are spread at the top level), useTwilight().store, or getTwilightContext().settings.store in a loader or head. It is the store of the store settings response as sent: the engine does not reshape it.

  • url includes the language and, on a shared Salla host, the store username: https://demostore.salla.sa/ar/dev-vgckq3fssfhjewwi/ on the demo store. Link to pages with the engine Link and a path, rather than joining strings onto it.

  • On the demo store social values are full URLs, description is HTML, and settings.product.total_sold_enabled is the number 1, not true.

  • scope describes the markets or branches a shopper picks from. When it is set, MasterLayout renders Salla's scopes component (its selection mandatory when display_as is popup, otherwise optional) and the engine Header a button labelled with scope.name that opens it. The demo store sends scope: null.

  • apps feeds resolveAppStoreUrl (@salla.sa/twilight-theme-engine/utils), and settings.opening_hours feeds useOpeningHours.

Gotchas

  • settings.buy_now and settings.installments are typed string | null, but the demo store sends an object and an empty array. An empty array is truthy, so if (settings.installments) runs when there are no installments. Test Array.isArray(value) ? value.length > 0 : Boolean(value).

  • settings.product.filters is typed boolean | null; the demo store sends an object of switches, one per filter ({ category_id: true, price: true, … }).

  • meta.title, meta.description and meta.keywords arrive as empty strings when the merchant left them blank (demo store), so store.meta?.title ?? store.name gives "". Use ||, as the engine buildBaseHead does.

  • settings.opening_hours, settings.features and settings.keys.sift are marked "TODO: add to API response" in the source and are absent on the demo store, so code that reads them sees undefined.

  • The response also carries fields the type does not declare, such as shipping, ratings and support_pickup on the store, and settings.cart.multi_coupon (demo store). Reading them needs a cast, and the engine promises nothing about them.

Related

Source and docs