Components and props
A component is a function that returns JSX, props are its arguments, and the engine's ProductCard works the same way.
A component is a JavaScript function that returns JSX. Its name starts with a capital letter, and you use it like a tag: <Badge />. That is how React tells your components apart from HTML elements, which are lowercase.
Props (short for properties) are the function's argument: one object holding every attribute written on the tag. <Badge text="New" tone="sale" /> calls Badge with { text: 'New', tone: 'sale' }.
One component, different props
The three badges below are the same function called with different props. Change the controls and compare the tag you write with the call it stands for.
<Badge text="New arrival" />
Badge({ text: 'New arrival' })
import type { ReactNode } from 'react';
interface BadgeProps {
text: string;
tone?: 'new' | 'sale' | 'last';
children?: ReactNode; // whatever is written between <Badge> and </Badge>
}
export function Badge({ text, tone = 'new', children }: BadgeProps) {
return (
<span className={`badge badge--${tone}`}>
{children}
{text}
</span>
);
}
- Defaults are ordinary JavaScript:
tone = 'new'in the parameter list applies when the tag leavestoneout. - `children` is a prop too: whatever you put between the opening and closing tags.
- Props flow down. A parent decides them and a component only reads them. To change what a badge shows, the parent passes different props.
An engine component: ProductCard
The engine ships its own components, and you use them exactly like Badge. ProductCard draws one product: image, badges, price, rating, the wishlist heart and a real "Add to cart" button. Its main prop is product, one product object from the Salla API; the rest choose how it looks. Below, the store's newest products each go through the same component.
Loading products…
import { useQuery } from '@tanstack/react-query';
import { product } from '@salla.sa/twilight-theme-engine/api/product';
import { ProductCard } from '@salla.sa/twilight-theme-engine/components/product';
export function LatestProducts() {
const { data } = useQuery(product.queries.list({ source: 'latest', perPage: 8 }));
return (
<div className="s-products-list-wrapper s-products-list-vertical-cards">
{data?.items.map((item, index) => (
<ProductCard
key={item.id}
product={item}
index={index}
imagePriority={index < 2}
/>
))}
</div>
);
}
The Code for these values tab shows only the props you changed: a prop left at its default does not need writing. The products themselves come from useQuery, which the lesson Loading data explains.
In engine terms
ProductCardand itsProductCardPropstype come from@salla.sa/twilight-theme-engine/components/product. Both the exported component and its inner content are wrapped inmemo: when a parent re-renders with props that are equal one by one (Object.is), the card skips its render. The context and the wishlist it reads can still re-render it (src/components/product/ProductCard.tsx).keylooks like a prop, but React keeps it for itself: your function never receives it. In a list, use a stable id such asproduct.idrather than the array index, so React does not mix items up when the list is reordered.- A component must be pure while rendering: the same props give the same JSX, with no requests or DOM writes during the call. Side effects belong in event handlers or
useEffect, covered in State and re-rendering. - Reference: ProductCard, and the Product type it receives.