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

useProduct

hookBeginnerserverbrowserlive demo

Keeps a live copy of a product that follows price and stock changes from Salla option pickers, and can reload its details.

import { useProduct, UseProductOptions, UseProductResult } from '@salla.sa/twilight-theme-engine/hooks/useProduct';

In plain words

A product page starts with the product data the server loaded. When a shopper picks a size or color, the price or stock can change. useProduct(product) gives you a product that updates itself when Salla's option components report a new price, or report that the chosen combination is unavailable.

reload() asks the Salla SDK for the product again and replaces the copy.

Signature

function useProduct(initialProduct: Product, options?: UseProductOptions): UseProductResult

interface UseProductOptions {
  isPreview?: boolean;  // true while initialProduct is a list-card stand-in
}

interface UseProductResult {
  product: Product;
  reload: () => Promise<void>;
}

Try it live

useProduct() keeps its own copy of the newest product in this store, starting from the list data.Try this: press reload(): the copy is replaced by the Salla SDK product details, so images changes from "not loaded" to a count.
Storefront canvas · ar · RTL

Loading a product…

What a theme writes
import { useProduct } from '@salla.sa/twilight-theme-engine/hooks/useProduct';
import { useMoney } from '@salla.sa/twilight-theme-engine/hooks/useMoney';
import type { Product } from '@salla.sa/twilight-theme-engine/types';

export function LivePrice({ initial }: { initial: Product }) {
  const { product } = useProduct(initial); // follows the SDK's option and price events
  const { format } = useMoney();
  return (
    <p>
      {format(product.price, { currency: product.currency })}
      {product.is_out_of_stock && <span> · out of stock</span>}
    </p>
  );
}

Example

app/components/product/LivePrice.tsx
import { useProduct } from '@salla.sa/twilight-theme-engine/hooks/useProduct';
import { useMoney } from '@salla.sa/twilight-theme-engine/hooks/useMoney';
import type { Product } from '@salla.sa/twilight-theme-engine/types';

export function LivePrice({ initial }: { initial: Product }) {
  const { product } = useProduct(initial);
  const { format } = useMoney();

  return (
    <div className="live-price">
      {format(product.price, { currency: product.currency })}
      {product.is_out_of_stock && <p>This combination is out of stock.</p>}
    </div>
  );
}

How it behaves

  • On the server, and until effects run, product is simply initialProduct. All subscriptions are made in effects, in the browser.

  • Salla.product.event.onPriceUpdated merges price, sale_price, regular_price, is_on_sale (from has_sale_price) and weight, and sets is_out_of_stock to false. product::price.updated.failed sets is_out_of_stock to true. product:options.updated calls reload().

  • reload() calls Salla.api.product.getDetails(String(product.id), ['images', 'category']) and, on success, replaces the whole state with response.data. Network errors are ignored.

  • The state is re-seeded from initialProduct only when initialProduct.id or options.isPreview changes, never because a new object with the same id arrived. That keeps live option and price changes from being thrown away by a refetch.

  • Pass isPreview: true while initialProduct is a list-card stand-in (for example the placeholder data of product.queries.detail) so the hook re-seeds when the full details for the same id arrive.

  • The engine's ProductPage uses it for the whole product page.

Gotchas

  • Props that change without a new id do not reach product. If you refetch the same product and want the new data, pass isPreview (true, then false) or remount the component with a key.

  • reload() asks only for images and category. The Store API then omits options, skus, brand, tags and rating (checked against the demo store), and the reloaded product replaces yours whole, so components reading those fields lose them.

  • Every subscription needs window.Salla when its effect first runs. If the SDK loads later, the price listeners are never attached.

  • Price events come from Salla's product web components. Plain HTML inputs for options fire nothing, so the price will not follow them.

Related

Source and docs