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

FixedProducts

componentBeginnerserverbrowserlive demo

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 | null

Try it live

A fixed grid of product cards. Like the slider, it fetches its own products from the source you name.Try this: set limit to 8: still four cards. limit only changes how many products are requested.
Storefront canvas · ar · RTL
Controls
Empty hides the title row and its link.
isVertical
What a theme writes
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

app/components/home/Deals.tsx
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 engine Link, so it needs the router.

  • The cards are ProductCard with no props besides product: a product:card replacement applies here too.

Gotchas

  • limit never shows more than four cards: it only sets perPage on the request, and the grid renders products.slice(0, 4).

  • An empty result renders null, title included.

  • The demo store sends this block with limit: 4 and products: { source: "latest" }, exactly the shape above. Blocks written by hand must keep products an object: data.products.source is read without a guard.

Related

Source and docs