FixedProducts
A home block that fetches products from a source and shows up to four ProductCards in a fixed grid, with an optional title.
import { FixedProducts } from '@salla.sa/twilight-theme-engine/components/home';In plain words
Like the slider, but still: a grid of at most four product cards under a title. You name the source (latest, offers…) and it loads the products itself.
isVertical chooses tall cards (the default) or wide, side-by-side cards.
Signature
function FixedProducts(props: {
data: {
products: { source: ProductsListSource; source_value?: number[] | null };
title?: string; // no title: no title row and no "View All" link
limit?: number | string; // sent as perPage
display_all_url?: string | null;
isVertical?: boolean; // true
[key: string]: unknown;
};
}): JSX.Element | nullTry it live
import { FixedProducts } from '@salla.sa/twilight-theme-engine/components/home';
export function LatestGrid() {
return (
<FixedProducts
data={{
products: { source: 'latest' },
title: 'Latest products',
limit: 4,
display_all_url: '/latest-products',
}}
/>
);
}
Example
import { FixedProducts } from '@salla.sa/twilight-theme-engine/components/home';
export function Deals() {
return (
<FixedProducts
data={{
products: { source: 'offers' },
title: 'Deals of the week',
display_all_url: '/offers',
isVertical: false,
}}
/>
);
}
How it behaves
It reads
useQuery(product.queries.list({ source, sourceValue, perPage: limit })), so the result is cached and shared with any other query using the same parameters and branch scope. Prefetch that query in a route loader and it renders on the server; otherwise products arrive after hydration.While the query loads it shows
FixedProductsSkeleton. The "View All" link is the engineLink, so it needs the router.The cards are
ProductCardwith no props besidesproduct: aproduct:cardreplacement applies here too.
Gotchas
limitnever shows more than four cards: it only setsperPageon the request, and the grid rendersproducts.slice(0, 4).An empty result renders
null, title included.The demo store sends this block with
limit: 4andproducts: { source: "latest" }, exactly the shape above. Blocks written by hand must keepproductsan object:data.products.sourceis read without a guard.