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

Components and props

Beginner8 min

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.

New arrivalFree deliveryOnly 2 left
What you write
<Badge text="New arrival" />
What React does with it (roughly)
Badge({ text: 'New arrival' })
app/components/Badge.tsx
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 leaves tone out.
  • `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.

The store's newest products, each drawn by the engine's ProductCard. Add to cart is the real Salla button.Try this: switch the layout to horizontal and watch the heart button move from the image to the footer; then tick withoutAddButton.
Real requests to the demo store
Storefront canvas · en · LTR

Loading products…

Controls
withShadow
withQuantityShows "Remained N" or "Out of Stock" when no promotion title takes the badge.
withoutAddButton
What a theme writes
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
  • ProductCard and its ProductCardProps type come from @salla.sa/twilight-theme-engine/components/product. Both the exported component and its inner content are wrapped in memo: 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).
  • key looks like a prop, but React keeps it for itself: your function never receives it. In a list, use a stable id such as product.id rather 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.
Check yourself

Inside function Badge({ text }), where does text come from?