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

What a hook is

Beginner6 min

Functions whose name starts with use: what they give a component, and the two rules for calling them.

You already know functions. A component is a function that returns what should appear on the page. A hook is a function a component calls to *get* something: a value to remember between clicks, the store's data, a price formatter.

Hooks are easy to spot: their name starts with use. You call them at the top of your component, never inside an if or a loop, so React can match each call to the same slot every time the component runs.

First, a hook from React itself

useState gives a component a value it remembers, and a function to change it. Changing it makes React run the component function again, which is how the screen updates.

Counter.tsx
import { useState } from 'react';

export function Counter() {
  const [count, setCount] = useState(0); // a hook: always at the top
  return <button onClick={() => setCount(count + 1)}>Clicked {count} times</button>;
}
This button is a component. Each click runs its function again.

Now a hook from the engine

The engine's hooks work the same way. useTheme() hands your component the merchant's theme choices: colors, font, settings. Below, it runs against the real demo store.

The merchant's theme colors, font and settings, as the store answers with them.Try this: switch the language pill (top bar, or the ☰ menu on a phone) to en and watch isRTL change.
Storefront canvas · en · LTR
color.primary #ed1c24
color.text #000000
color.reverse_primary #6e0000

font: Dubai · isRTL: false

settings: {…} 26 keys
font: {…} 5 keys
imageZoom: false
show_tags: true
store_color: "#ed1c24"
homepage_type: "custom"
is_custom_css: true
theme_version: "1.188.0"
footer_is_dark: false
topnav_is_dark: false
important_links: true
store_font_type: "default"
enable_more_menu: true
footer_menu_type: "default"
header_is_sticky: true
header_menu_type: "default"
default_font_name: "خط سلة الافتراضي (جديد)"
translations_hash: "1789908891"
sticky_add_to_cart: true
customization_css_ver: 1778680971
is_more_button_enabled: true
slider_background_size: "cover"
vertical_fixed_products: false
enable_add_product_toast: true
squar_photo_bg_image_size: "cover"
is_show_more_detail_enabled: true
is_breadcrumbs_enabled: true
What a theme writes
import { useTheme } from '@salla.sa/twilight-theme-engine/hooks/useTheme';

export function PrimaryButton({ children }: { children: React.ReactNode }) {
  const { color } = useTheme();
  return (
    <button style={{ background: color.primary, color: color.reverse_primary }}>
      {children}
    </button>
  );
}

The two rules

  • Call hooks only at the top level of a component (or of another hook), never inside conditions, loops or event handlers.
  • Call them only from components or other hooks, not from plain functions.

Break a rule and React either throws or mixes up which value belongs to which call. The ESLint plugin eslint-plugin-react-hooks checks both rules as you type.

Your own hooks

A function of your own whose name starts with use and that calls other hooks is a hook too. It is how you package a piece of behaviour to reuse: the lesson Putting it together writes useCartSubtotal(), which follows the cart total through the Salla SDK.

app/hooks/useStoreGreeting.ts
import { useStore } from '@salla.sa/twilight-theme-engine/hooks/useStore';
import { useTranslation } from '@salla.sa/twilight-theme-engine/i18n';

// A custom hook: a name starting with "use", built from other hooks.
export function useStoreGreeting() {
  const { name } = useStore();
  const { isRTL } = useTranslation();
  return isRTL ? `أهلًا بك في ${name}` : `Welcome to ${name}`;
}
In engine terms
  • Most engine hooks have their own import path, such as @salla.sa/twilight-theme-engine/hooks/useStore or /hooks/useMoney. useIsClient, useDate and useNumber exist only in the /hooks barrel; useTranslation comes from /i18n and useTwilight from the package root.
  • React matches hook calls to their stored values by call order, which is why the order must be the same on every render. react-hooks/rules-of-hooks reports a conditional call as an error. This repository's root ESLint config turns it on; the engine's twilightEslintConfig (/eslint) does not include it, so add eslint-plugin-react-hooks to your theme's own config.
  • Most engine hooks read TwilightProvider's context, and useTwilight() throws outside it, so use them in components rendered inside the provider: in a theme, that is every page.
Check yourself

Which of these is a hook?

Next, see every engine hook in the Hooks reference, or continue the track.