product.find
Fetches one product with everything a product page needs: images, options, SKUs, brand, tags, rating and bundle contents.
import { product } from '@salla.sa/twilight-theme-engine/api/product';In plain words
A product page needs far more than a product card: every photo, the size and color options, stock per option. product.find(id) loads all of that for one product id.
product.findOrThrow(id) does the same for a loader but turns any failure into the store's 404 page, and product.queries.detail(id) is the version for useQuery in a component. While the details load, that query shows the product card you already have from a list, so the page never starts blank.
Signature
product.find(id: string): Promise<Product>
product.findOrThrow(id: string): Promise<Product> // any failure → NotFoundError (the 404 page)
product.queries.detail(id: string)
// queryOptions, key ['products', 'detail', id, { scope }]
// placeholderData: () => getProductPreview(id)Try it live
Loading the latest products…
import { useQuery } from '@tanstack/react-query';
import { product } from '@salla.sa/twilight-theme-engine/api/product';
export function ProductSummary({ id }: { id: string }) {
const { data, isPlaceholderData } = useQuery(product.queries.detail(id));
if (!data) return <p>Loading…</p>;
return (
<article aria-busy={isPlaceholderData}>
<h2>{data.name}</h2>
{/* A list card stands in until the details arrive: it has no images[] yet. */}
{isPlaceholderData ? <p>Loading details…</p> : <p>{data.images?.length ?? 0} photos</p>}
</article>
);
}
Example
import { useQuery } from '@tanstack/react-query';
import { product } from '@salla.sa/twilight-theme-engine/api/product';
import { useMoney } from '@salla.sa/twilight-theme-engine/hooks/useMoney';
export function QuickView({ productId }: { productId: string }) {
const { format } = useMoney();
const { data, isPlaceholderData, isError } = useQuery(product.queries.detail(productId));
if (isError) return <p>This product is no longer available.</p>;
if (!data) return <p>Loading…</p>;
return (
<div className="quick-view">
<img src={data.image.url} alt={data.image.alt ?? data.name} />
<h2>{data.name}</h2>
<p>{format(data.price)}</p>
{!isPlaceholderData && <p>{data.options?.length ?? 0} options to choose from</p>}
</div>
);
}
How it behaves
Endpoint:
GET products/{id}/detailswithwith[]=images, category, rating, notify_availability, tags, brand, options, skus, skus_availability, included_products, bundle and sold_quantity. Public.The engine product page loader calls
product.findOrThrow(params.id).findOrThrowisorThrow(product.find(id)), so a timeout or a 500 also becomes the 404 page. Callfindand catch yourself when those must look different.The detail key includes the active branch scope, because stock and availability are answered per branch.
placeholderDatais a stand-in, not cached data:isPlaceholderDataistrueand the request still runs. The stand-in is a list card, so fields only this endpoint sends (images,options,skus) are missing until it resolves.
Gotchas
Options from this endpoint carry their choices in
options[].details;options[].values(marked as the listing shape in the type) is absent. Code written forvaluesfinds nothing on a product page.Reading
data.imageswhileisPlaceholderDataistruegivesundefined, not an empty gallery that later grows. Render the card fields first and wait forisPlaceholderData === falsebefore anything that needs the details.
Related
Lists products from any source (latest, offers, a category, a brand, a search) with page size, sort, filters and cursor pagination.
Product preview cacheThe list-card cache that lets a product's details paint at once from the card you already have, before the full details arrive.
orThrow, orUnauthorizedWrap an API call in a loader: orThrow turns any failure into a not-found error, orUnauthorized turns a 401 or 403 into the unauthorized flow.
useProductKeeps a live copy of a product that follows price and stock changes from Salla option pickers, and can reload its details.
Source and docs
- Engine source:
packages/theme-engine/src/api/product.ts