Margherita
8.50 €
AvailableNikita Goluban
Full-Stack Developer Logistics & Marketing Mechanical & Construction Skills Woodwork & Metalwork Polyglot
React.js + Next.js · Module 2. React Basics · Lesson 11
Pizza Palace components often need different styles depending on their props or current state:
Instead of building complicated className strings manually, we can use the small clsx library. clsx combines class names and includes them conditionally.
clsx.clsx() combines class names.clsx with CSS Modules.clsx mistakes.REACT · 01
Class composition means assigning several CSS classes to one element.
.button {
padding: 10px 20px;
border: 0;
border-radius: 8px;
}
.primary {
background-color: #d92d20;
color: white;
}
function OrderButton() {
return (
<button className="button primary">
Add to cart
</button>
);
}
The base class controls shared button styling, while the variant class controls its colour.
When classes must change at runtime, clsx makes this composition easier to read.
REACT · 02
clsx is a small JavaScript utility that builds class-name strings. Install it inside the React project:
npm install clsx
Import its default export:
import clsx from "clsx";
Then combine class names:
const className = clsx("button", "primary");
console.log(className);
button primary
clsx only creates a string. It does not install visual styles or generate CSS rules.
REACT · 03
Call clsx() inside the JSX expression assigned to className.
.button {
padding: 10px 20px;
border: 0;
border-radius: 8px;
}
.primary {
background-color: #d92d20;
color: white;
}
import clsx from "clsx";
export default function OrderButton() {
return (
<button className={clsx("button", "primary")}>
Order Margherita
</button>
);
}
The browser receives markup equivalent to <button class="button primary">.
clsx() supplies the two existing CSS class names.REACT · 04
Use a logical AND expression when a class should appear only under one condition.
import clsx from "clsx";
const isSelected = true;
const className = clsx(
"pizzaCard",
isSelected && "selected"
);
console.log(className);
pizzaCard selected
If isSelected is false, the expression returns false, which clsx ignores:
pizzaCard
The base class is always included; the state class is conditional. clsx also ignores null, undefined, and empty strings.
REACT · 05
Several independent conditions can be composed in one call.
import clsx from "clsx";
const isSelected = true;
const isSoldOut = false;
const hasDiscount = true;
const className = clsx(
"pizzaCard",
isSelected && "selected",
isSoldOut && "soldOut",
hasDiscount && "discounted"
);
console.log(className);
pizzaCard selected discounted
Each condition is evaluated separately. This is clearer than a long chain of manual string concatenations.
REACT · 06
With CSS Modules, clsx combines the generated class-name strings stored in the imported object.
.alert {
padding: 12px 16px;
border-radius: 8px;
color: white;
background-color: #475467;
}
.success {
background-color: #16803c;
}
.error {
background-color: #b42318;
}
import clsx from "clsx";
import styles from "./OrderAlert.module.css";
export default function OrderAlert() {
return (
<p className={clsx(styles.alert, styles.success)}>
Your pizza order was accepted!
</p>
);
}
REACT · 07
One alert component can support default, success, and error appearances.
import clsx from "clsx";
import styles from "./OrderAlert.module.css";
interface OrderAlertProps {
type?: "success" | "error";
children: string;
}
export default function OrderAlert(props: OrderAlertProps) {
return (
<p
className={clsx(
styles.alert,
props.type && styles[props.type]
)}
>
{props.children}
</p>
);
}
The union type permits only "success", "error", or an omitted value. styles.alert is always included. The variant class is included only when type exists.
This is a good use of props: the parent chooses the meaning, and the component translates that meaning into presentation.
REACT · 08
Suppose Vite provides this simplified mapping:
const styles = {
alert: "_alert_a1b2c_1",
success: "_success_a1b2c_2",
error: "_error_a1b2c_3"
};
Dot notation uses a fixed property:
styles.success
Bracket notation can use a dynamic property name:
const type = "success";
styles[type];
When props.type is "error", styles[props.type] is equivalent to styles.error.
The check is important when the prop is optional:
props.type && styles[props.type]
Without a value, the expression becomes undefined, and clsx safely leaves the variant class out.
REACT · 09
Use the same typed component for three order states.
import OrderAlert from "./components/OrderAlert";
export default function App() {
return (
<main>
<h1>Order Status</h1>
<OrderAlert>Check your delivery details.</OrderAlert>
<OrderAlert type="success">
Your pizza order was accepted!
</OrderAlert>
<OrderAlert type="error">
Payment failed. Please try again.
</OrderAlert>
</main>
);
}
REACT · 10
Simple concatenation can work:
const className = styles.alert + " " + styles.success;
But conditions quickly become difficult to scan:
const className =
styles.card +
(isSelected ? " " + styles.selected : "") +
(isSoldOut ? " " + styles.soldOut : "");
With clsx, each rule is visible on its own line:
const className = clsx(
styles.card,
isSelected && styles.selected,
isSoldOut && styles.soldOut
);
clsx handles spacing and ignores false values.
REACT · 11
clsx also accepts objects. Each key is a class name and its value decides whether the class is included.
import clsx from "clsx";
const isSelected = true;
const isSoldOut = false;
const className = clsx({
pizzaCard: true,
selected: isSelected,
soldOut: isSoldOut
});
console.log(className);
pizzaCard selected
With CSS Modules, use computed property names:
const className = clsx({
[styles.card]: true,
[styles.selected]: isSelected,
[styles.soldOut]: isSoldOut
});
Both the argument-list form and object form are valid. Choose the one that is clearest for the component.
REACT · 12
Create one button component for primary, secondary, and disabled actions.
.button {
padding: 10px 20px;
border: 0;
border-radius: 8px;
cursor: pointer;
font: inherit;
font-weight: 700;
}
.primary {
background-color: #d92d20;
color: white;
}
.secondary {
background-color: #e4e7ec;
color: #1d2939;
}
.disabled {
opacity: 0.5;
cursor: not-allowed;
}
import clsx from "clsx";
import styles from "./OrderButton.module.css";
interface OrderButtonProps {
text: string;
variant?: "primary" | "secondary";
disabled?: boolean;
}
export default function OrderButton(props: OrderButtonProps) {
return (
<button
type="button"
disabled={props.disabled}
className={clsx(
styles.button,
props.variant && styles[props.variant],
props.disabled && styles.disabled
)}
>
{props.text}
</button>
);
}
import OrderButton from "./components/OrderButton";
export default function App() {
return (
<main>
<OrderButton text="Add to cart" variant="primary" />
<OrderButton text="View ingredients" variant="secondary" />
<OrderButton
text="Sold out"
variant="primary"
disabled={true}
/>
</main>
);
}
A disabled-looking class alone does not prevent interaction. The disabled attribute supplies the correct browser behaviour.
REACT · 13
Pizza availability changes both text and styling.
.card {
padding: 20px;
border: 1px solid #e4e7ec;
border-radius: 12px;
}
.status {
display: inline-block;
padding: 6px 10px;
border-radius: 999px;
font-weight: 700;
}
.available {
color: #116329;
background-color: #dcfce7;
}
.unavailable {
color: #991b1b;
background-color: #fee2e2;
}
import clsx from "clsx";
import styles from "./PizzaCard.module.css";
interface PizzaCardProps {
name: string;
price: number;
available: boolean;
}
export default function PizzaCard(props: PizzaCardProps) {
return (
<article className={styles.card}>
<h2>{props.name}</h2>
<p>{props.price.toFixed(2)} €</p>
<p
className={clsx(
styles.status,
props.available
? styles.available
: styles.unavailable
)}
>
{props.available ? "Available" : "Sold out"}
</p>
</article>
);
}
import PizzaCard from "./components/PizzaCard";
export default function App() {
return (
<main>
<h1>Pizza Palace Menu</h1>
<PizzaCard name="Margherita" price={8.5} available={true} />
<PizzaCard name="Truffle Pizza" price={14} available={false} />
</main>
);
}
REACT · 14
Run npm install clsx, then add import clsx from "clsx" in every component that calls it.
clsx("button", "primary") returns a string. The .button and .primary rules must already exist.
Incorrect:
className={clsx(props.type && styles[props.type])}
Correct:
className={clsx(
styles.alert,
props.type && styles[props.type]
)}
styles.type looks for a class literally named type. Use styles[props.type] to read the class named by the prop.
When type can be undefined, guard the dynamic access with props.type && styles[props.type].
If .warning does not exist, styles.warning is undefined. clsx ignores it, so the expected design silently disappears.
A CSS class changes appearance but not behaviour. Also pass disabled={true} to a native button.
Do not write className={isSelected}. className needs a string; use className={clsx(isSelected && styles.selected)}.
REACT · 15
What does clsx("button", "primary") return? Expected result: button primary.
Create const isSelected = true and include "selected" only when it is true.
Create a module with .card and .featured. Apply .card always and .featured conditionally.
Create a typed OrderAlert whose optional type is "success" | "error". Render all three appearances.
Rewrite a clsx(styles.card, isSoldOut && styles.soldOut) call using the object form and computed keys.
Create primary and secondary variants plus a real disabled state. Confirm that the disabled button cannot be activated.
Use one PizzaCard component to render an available pizza and a sold-out pizza with different status classes.
REACT · 16
clsx() returns a space-separated class-name string.clsx does not create CSS rules.styles.card are generated class-name strings.clsx object form maps class names to Boolean conditions.disabled attribute.clsx translates that state into a class list.REACT · 17
Next lesson: Begin Module 3 with React forms, FormData, callback props, and Pizza Palace order submission.