product

Fetch one product with its details, or a cursor-paged list of products from any source (latest, offers, category, search and more).

Use product for anything that shows products: a product page, a listing, a slider, a search.

app/routes/sale.tsx
import { getTwilightContext } from '@salla.sa/twilight-theme-engine/tanstack';
import { product } from '@salla.sa/twilight-theme-engine/api/product';

loader: () => {
  const { queryClient } = getTwilightContext();
  return queryClient.fetchQuery(product.queries.list({ source: 'offers' }));
},
app/components/CategoryProducts.tsx
import { useQuery } from '@tanstack/react-query';
import { product } from '@salla.sa/twilight-theme-engine/api/product';

export function CategoryProducts({ categoryId }: { categoryId?: number }) {
  const { data } = useQuery({
    ...product.queries.list({ source: 'categories', sourceValue: [categoryId!] }),
    enabled: categoryId !== undefined,
  });
  const items = data?.items ?? [];

  return <p>{items.length} products</p>;
}

Methods

MethodParametersReturnsRequestLogin needed
findid: stringPromise<Product>GET products/{id}/details?with[]=... (images, category, brand, options, skus, bundle and more)No
findOrThrowid: stringPromise<Product>Same as findNo
listparams: ProductsListParamsPromise<ProductsListResult>GET products?source=...&per_page=...No (source: 'wishlist' and 'reorder' are customer-specific)

ProductsListParams:

ParamDescription
sourceRequired. One of brands, categories, json, landing-page, latest, offers, recently, related, selected, tags, top-rated, wishlist, search, sales, reorder, best_selling
sourceValueThe ids the source reads (category, brand, tag or product ids), as an array such as [708738657]; sent as source_value[]
keywordThe search words when source is 'search'
page, cursorPaging. Use cursor (from next); page is sent only if you set it
perPageItems per page, default 16
sort, filterable, filtersSorting and filters
signalAn AbortSignal

The result is { items: Product[]; next: string | null; filters?: Filter[] }. Pass next back as cursor for the following page; it is null on the last page.

Queries

QueryKey
product.queries.detail(id)['products', 'detail', id, { scope }]
product.queries.list(params)['products', 'list', params, { scope }]

Both keys include the active scope (the customer's branch) because stock depends on it. Neither includes the locale; see Authentication and scope.

Gotchas

  • find throws on an unknown id. In a loader use findOrThrow to get a not-found page for a 404 or 410 (other errors still throw).
  • For source: 'search' put the words in keyword. Every other source needs sourceValue as ids.
  • With source: 'categories', a category_id entry in filters replaces sourceValue.
  • Items returned by list are remembered, so queries.detail(id) can show the card data as placeholder while the full details load.
  • There are no mutations. Invalidate ['products'] if the customer's branch changes.

See also

On this page