0Pricing
Tailwind CSS Academy · Lezione

Classi condizionali in React

Utilizzi clsx o tailwind-merge per applicare condizionalmente e combinare in modo sicuro le classi Tailwind in base alle props e allo stato del componente, evitando conflitti tra classi.

Classi condizionali in React è una lezione Tailwind CSS Academy gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Tailwind CSS Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Tailwind CSS Academy include 4 lezioni in totale.

Parti di questa lezione non sono ancora state tradotte e vengono mostrate in inglese.

The Problem With String Concatenation

Applying Tailwind classes conditionally in React is straightforward at first, but naive string concatenation quickly becomes error-prone. Concatenating strings with template literals can accidentally include undefined or false in the className, leading to invalid class names in the DOM. More seriously, conflicting Tailwind utilities — like text-blue-500 and text-red-500 — do not cancel each other out; the order in the stylesheet determines which wins, not the order in your className string.

// Problematic — may include 'false' or 'undefined' in class list
function Button({ disabled, primary }) {
  return (
    <button
      className={'px-4 py-2 rounded ' +
        primary && 'bg-blue-500 text-white' +  // bug: && short-circuit
        disabled && 'opacity-50'               // 'false' in string
      }
    >
      Click me
    </button>
  );
}

Using clsx for Conditional Classes

clsx is a tiny utility that safely constructs className strings from conditionals, objects, and arrays. It filters out falsy values like false, null, and undefined, so your DOM always gets clean class names. You can pass strings, objects with boolean values, or arrays of either. Install it with npm install clsx and import it wherever you need conditional class logic.

import clsx from 'clsx';

function Button({ disabled, primary, className }) {
  return (
    <button
      className={clsx(
        'px-4 py-2 rounded font-medium transition-colors',
        primary && 'bg-blue-600 text-white hover:bg-blue-700',
        !primary && 'bg-gray-100 text-gray-900 hover:bg-gray-200',
        disabled && 'opacity-50 cursor-not-allowed',
        className  // allow caller to pass extra classes
      )}
      disabled={disabled}
    >
      Click me
    </button>
  );
}

clsx Object Syntax

clsx accepts an object syntax where keys are class names and values are boolean conditions. This is especially readable when you have many conditional classes grouped by concern. You can mix the object syntax with positional string arguments in the same clsx call, making it easy to separate unconditional base classes from conditional variant classes.

import clsx from 'clsx';

function Alert({ type }) {
  return (
    <div
      className={clsx(
        // Unconditional base classes
        'rounded-lg border p-4 flex items-start gap-3',
        // Conditional classes via object syntax
        {
          'bg-red-50 border-red-200 text-red-800': type === 'error',
          'bg-yellow-50 border-yellow-200 text-yellow-800': type === 'warning',
          'bg-green-50 border-green-200 text-green-800': type === 'success',
          'bg-blue-50 border-blue-200 text-blue-800': type === 'info'
        }
      )}
    >
      {/* alert content */}
    </div>
  );
}

The Class Conflict Problem

Even with clsx producing clean class strings, Tailwind class conflicts remain a problem. If a parent applies text-blue-500 and a child or override applies text-red-500, both classes appear in the DOM. CSS cascade order (not DOM order) determines which wins — whichever Tailwind generated last in the stylesheet. This makes overriding behavior unpredictable, especially when passing className props from callers.

// Class conflict example
function Badge({ className }) {
  return (
    <span className={clsx('bg-blue-500 text-white px-2 py-0.5 rounded', className)}>
      Tag
    </span>
  );
}

// Caller tries to override background
<Badge className="bg-red-500" />
// Result: both bg-blue-500 AND bg-red-500 in the DOM
// Which one wins depends on Tailwind's stylesheet order, not your intent

tailwind-merge Solves Conflicts

tailwind-merge (twMerge) is aware of Tailwind's utility groups and ensures the last value for any conflicting property wins. It understands that bg-blue-500 and bg-red-500 both set background-color, so it keeps only the last one. Install it with npm install tailwind-merge and wrap your clsx calls with twMerge for predictable override behavior.

import { twMerge } from 'tailwind-merge';
import clsx from 'clsx';

function Badge({ className }) {
  return (
    <span
      className={twMerge('bg-blue-500 text-white px-2 py-0.5 rounded', className)}
    >
      Tag
    </span>
  );
}

// Caller override now works correctly!
<Badge className="bg-red-500" />
// Result: only bg-red-500 (bg-blue-500 is removed by twMerge)

The cn() Helper Pattern

In most Next.js and React projects, you see a utility function called cn() that combines clsx and tailwind-merge into a single convenient call. This pattern is so common that create-next-app with shadcn/ui includes it by default. Define it once in a utility file and import it everywhere you need conditional, conflict-free class handling.

// lib/utils.ts
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

// Usage in any component
import { cn } from '@/lib/utils';

function Card({ className, children }) {
  return (
    <div className={cn(
      'rounded-xl border bg-white shadow-sm p-6',
      className
    )}>
      {children}
    </div>
  );
}

Conditional Classes Based on State

The cn() helper shines when component state drives visual changes. A toggle switch, an active nav item, or a form field with validation errors all need classes that change based on JavaScript state. Using cn with object syntax makes the relationship between state and styles immediately readable — anyone reading the component can see exactly which classes apply under which conditions.

import { cn } from '@/lib/utils';

function NavItem({ href, active, children }) {
  return (
    <a
      href={href}
      className={cn(
        'flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium',
        'transition-colors duration-150',
        {
          'bg-blue-50 text-blue-700': active,
          'text-gray-600 hover:bg-gray-100 hover:text-gray-900': !active
        }
      )}
    >
      {children}
    </a>
  );
}

Passing className Props Safely

A key design decision for reusable components is how to handle the className prop. Using twMerge ensures that caller-provided classes properly override defaults. However, some classes should never be overrideable — like structural classes that define a component's layout. One pattern is to apply structural classes without merging and only merge presentational classes with the caller's input.

import { cn } from '@/lib/utils';

function Input({ className, hasError, ...props }) {
  return (
    <input
      className={cn(
        // Base structural: never overridden
        'block w-full rounded-lg border px-3 py-2 text-sm',
        'placeholder:text-gray-400 focus:outline-none focus:ring-2',
        // Conditional state classes
        hasError
          ? 'border-red-300 focus:ring-red-500'
          : 'border-gray-300 focus:ring-blue-500',
        // Caller overrides presentational classes
        className
      )}
      {...props}
    />
  );
}

Avoiding Dynamic Class Names

Tailwind's JIT engine detects class names by scanning source files as complete strings. Never construct class names dynamically by concatenating partial strings like 'text-' + color + '-500'. The JIT scanner will not detect these and the classes will be purged from the production bundle. Always use complete class name strings, even if that means a longer conditional expression.

// WRONG — JIT cannot detect these dynamic class names
const colors = { info: 'blue', error: 'red' };
<div className={'text-' + colors[type] + '-500'} />

// CORRECT — full class names that JIT can detect
const colorMap = {
  info: 'text-blue-500 bg-blue-50',
  error: 'text-red-500 bg-red-50',
  success: 'text-green-500 bg-green-50'
};
<div className={cn('rounded p-3', colorMap[type])} />

Memoizing Class Computations

When a component renders frequently and has complex className computations, consider memoizing the result with useMemo. The clsx and twMerge operations are fast, but on components that render hundreds of times per second (like virtual list items), even small savings add up. More commonly, extracting the class computation into a variable outside JSX improves readability regardless of performance.

import { useMemo } from 'react';
import { cn } from '@/lib/utils';

function ListItem({ selected, variant, className }) {
  const itemClasses = useMemo(() => cn(
    'flex items-center gap-3 px-4 py-3 cursor-pointer',
    'border-b border-gray-100 transition-colors',
    {
      'bg-blue-50 border-l-2 border-l-blue-500': selected,
      'hover:bg-gray-50': !selected,
      'opacity-50 pointer-events-none': variant === 'disabled'
    },
    className
  ), [selected, variant, className]);

  return <div className={itemClasses}>{/* content */}</div>;
}

Testing Conditional Class Logic

Write unit tests for components with complex conditional class logic to prevent regressions. Using React Testing Library, you can assert that specific Tailwind classes are present or absent based on prop values. This is especially important for accessibility-relevant classes — testing that a disabled button has cursor-not-allowed and opacity-50 provides confidence that UI affordances are correct.

// Button.test.tsx
import { render, screen } from '@testing-library/react';
import { Button } from './Button';

test('disabled button has correct classes', () => {
  const { container } = render(
    <Button disabled>Submit</Button>
  );
  const btn = container.firstChild;
  expect(btn.className).toContain('opacity-50');
  expect(btn.className).toContain('cursor-not-allowed');
});

test('primary variant applies correct colors', () => {
  const { container } = render(
    <Button primary>Submit</Button>
  );
  expect(container.firstChild.className).toContain('bg-blue-600');
});

Quick Check

Test your understanding of Tailwind CSS Mastery concepts from this lesson.

Lesson Recap

In this lesson you learned: clsx safely builds conditional class strings by filtering falsy values, tailwind-merge resolves conflicting Tailwind utilities so the last-applied wins, and the cn() helper combines both into a single call used throughout your React project. Next up we explore Component Variants with CVA for typed variant APIs.

Domande Frequenti

La lezione «Classi condizionali in React» è gratuita?

Sì — il testo completo di «Classi condizionali in React» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Tailwind CSS Academy, passa a CoddyKit PRO. Il corso Tailwind CSS Academy include 4 lezioni in totale.

Cosa imparerò in «Classi condizionali in React»?

Utilizzi clsx o tailwind-merge per applicare condizionalmente e combinare in modo sicuro le classi Tailwind in base alle props e allo stato del componente, evitando conflitti tra classi. Eserciti Tailwind CSS Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Tailwind CSS Academy?

Non è richiesta alcuna esperienza precedente. Tailwind CSS Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.

Quanto tempo richiede la lezione «Classi condizionali in React»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Tailwind CSS Academy?

Sì. Ogni lezione Tailwind CSS Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Configurare Tailwind in Next.js
  2. Classi condizionali in React
  3. Varianti dei componenti con CVA
  4. Evitare conflitti tra classi con tailwind-merge
← Torna a Tailwind CSS Academy