FeaturedProductsStyle1, 2 and 3
Three featured-products home blocks: tabs with a main product, tabs that load their own products, and a large card beside small ones.
import { FeaturedProductsStyle1, FeaturedProductsStyle2, FeaturedProductsStyle3 } from '@salla.sa/twilight-theme-engine/components/home';In plain words
Three looks for the same idea, a highlighted group of products. The API says which one a block wants with its view_style.
- Style 1: tabs of products, with an optional large "main product" beside them. Products come inside the data.
- Style 2: tabs where each tab loads its own products from a source, as a slider or a grid.
- Style 3: sections with one big card for a featured product and up to three small ones. Products come inside the data.
Signature
const FeaturedProductsStyle1: MemoExoticComponent<(props: { data: {
items: { id?: string; title?: string; name?: string; products?: Product[] }[];
main_product?: { title?: string; product?: Product };
[key: string]: unknown;
} }) => JSX.Element | null>
const FeaturedProductsStyle2: MemoExoticComponent<(props: { data: {
items: { id?: string; title?: string; name?: string;
products: { source: ProductsListSource; source_value?: number | number[]; limit?: number } }[];
is_slider?: boolean; // true
[key: string]: unknown;
} }) => JSX.Element | null>
const FeaturedProductsStyle3: MemoExoticComponent<(props: { data: {
items: { id?: string; title?: string; featured_product?: Product; products?: Product[] }[];
[key: string]: unknown;
} }) => JSX.Element | null>Try it live
Loading products…
import { FeaturedProductsStyle3 } from '@salla.sa/twilight-theme-engine/components/home';
import type { Product } from '@salla.sa/twilight-theme-engine/types';
export function Picks({ hero, others }: { hero: Product; others: Product[] }) {
return (
<FeaturedProductsStyle3
data={{
items: [{ id: 'picks', title: 'Staff picks', featured_product: hero, products: others }],
}}
/>
);
}
Example
import { FeaturedProductsStyle2 } from '@salla.sa/twilight-theme-engine/components/home';
// Module scope: the same data object on every render.
const TABS = {
is_slider: false,
items: [
{ id: 'latest', title: 'Latest', products: { source: 'latest' as const, limit: 8 } },
{ id: 'offers', title: 'Offers', products: { source: 'offers' as const, limit: 8 } },
],
};
export function ProductTabs() {
return <FeaturedProductsStyle2 data={TABS} />;
}
How it behaves
They are registered as
featured-products:style1,:style2and:style3, so a block needsview_styleset to exactly that. With no items, each rendersnull.Style 1 shows 4 products per tab when there is a main product, 8 otherwise. Tabs switch by
id(the first item's is active); give every item a distinctid.Style 2 renders
ProductCards inSallaProductsSlider(default) orSallaProductsListwithis_slider: false. Every tab is rendered at once and hidden with CSS, so every tab fetches when the block mounts.Style 3 renders
ProductCardwithlayout="fullImage"forfeatured_productandlayout="minimal"for the others (the first three when a featured product exists, all otherwise), allwithShadow.
Gotchas
Style 1 draws each product as a
custom-salla-product-cardelement, which nothing in the engine or the reference theme defines (the Salla SDK only uses that tag when a theme defined it). In a React theme its cards are empty; prefer Style 3, or register your own component forfeatured-products:style1.In slider mode (the default), Style 2 builds each tab's loader during render, and
SallaProductsSliderfetches again whenever its loader changes: every re-render of the block, a tab click included, fetches every tab again. The grid (is_slider: false) loads once. Style 2 also readsitem.products.sourcewithout a guard, so an item withoutproductsthrows.The demo store sends its featured block with
view_style: "products_without_special_product", which none of the three keys match. See DefaultHomeComponents.packages/theme-engine/docs/HOME_COMPONENTS.md spells the styles
style-1,style-2andstyle-3, and showsitemsas bare products. The registered keys arestyle1tostyle3, and every style readsitemsas sections ({ id, title, products }); a block written from that page matches nothing.