Tailwind CSS Academy · Lektion

Villkorliga klasser i React

Använd clsx eller tailwind-merge för att villkorligt tillämpa och säkert slå samman Tailwind-klasser baserat på komponenters props och state, utan klasskonflikter.

Lektion 2 av 413 steg

Villkorliga klasser i React är en gratis lektion i Tailwind CSS Academy på CoddyKit. Detta är lektion 2 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Tailwind CSS Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Tailwind CSS Academy innehåller totalt 4 lektioner.

Problemet med strängkonkatenering

Att tillämpa Tailwind-klasser villkorligt i React är till en början enkelt, men naiv strängkonkatenering blir snabbt felbenägen. När strängar konkateneras med malliteraler kan undefined eller false av misstag inkluderas i className, vilket leder till ogiltiga klassnamn i DOM-trädet. Ännu allvarligare är att motstridiga Tailwind-verktygsklasser — som text-blue-500 och text-red-500 — inte tar ut varandra. Det är ordningen i stilmallen, inte ordningen i din className-sträng, som avgör vilken som vinner.

// 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>
  );
}

Använda clsx för villkorsstyrda klasser

clsx är ett litet verktyg som på ett säkert sätt bygger className-strängar från villkor, objekt och arrayer. Det filtrerar bort falska värden som false, null och undefined, så att DOM-trädet alltid får rena klassnamn. Du kan skicka in strängar, objekt med booleska värden eller arrayer av dessa. Installera det med npm install clsx och importera det där du behöver villkorslogik för klasser.

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-objektsyntax

clsx accepterar en objektsyntax där nycklarna är klassnamn och värdena är booleska villkor. Detta är särskilt lättläst när du har många villkorsstyrda klasser som grupperas efter användningsområde. Du kan blanda objektsyntax med positionella strängargument i samma clsx-anrop, vilket gör det enkelt att skilja ovillkorliga basklasser från villkorsstyrda variantklasser.

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>
  );
}

Problemet med klasskonflikter

Även när clsx skapar rena klasssträngar kvarstår konflikter mellan Tailwind-klasser som ett problem. Om en förälder tillämpar text-blue-500 och ett underordnat element eller en överskrivning tillämpar text-red-500, visas båda klasserna i DOM-trädet. CSS-cascadeordningen (inte DOM-ordningen) avgör vilken som vinner — det är den som Tailwind genererade sist i stilmallen. Det gör överskrivningsbeteendet oförutsägbart, särskilt när className-props skickas från anropande komponenter.

// 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 löser konflikter

tailwind-merge (twMerge) känner till Tailwinds verktygsgrupper och ser till att det sista värdet för en motstridig egenskap vinner. Det förstår att bg-blue-500 och bg-red-500 båda anger background-color, så endast den sista behålls. Installera det med npm install tailwind-merge och omslut dina clsx-anrop med twMerge för ett förutsägbart överskrivningsbeteende.

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)

Mönstret med hjälpfunktionen cn()

I de flesta Next.js- och React-projekt ser du en verktygsfunktion som heter cn() och som kombinerar clsx och tailwind-merge i ett enda praktiskt anrop. Mönstret är så vanligt att create-next-app med shadcn/ui inkluderar det som standard. Definiera funktionen en gång i en verktygsfil och importera den överallt där du behöver villkorsstyrd hantering av konfliktfria klasser.

// 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>
  );
}

Villkorsstyrda klasser baserade på tillstånd

Hjälpfunktionen cn() är särskilt användbar när komponentens tillstånd styr visuella förändringar. En växlingsknapp, ett aktivt navigeringsobjekt eller ett formulärfält med valideringsfel behöver alla klasser som ändras utifrån JavaScript-tillstånd. Att använda cn med objektsyntax gör sambandet mellan tillstånd och formatering omedelbart lättläst — alla som läser komponenten kan se exakt vilka klasser som tillämpas under vilka villkor.

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>
  );
}

Skicka className-props på ett säkert sätt

Ett viktigt designbeslut för återanvändbara komponenter är hur propen className ska hanteras. Med twMerge säkerställer du att klasser som tillhandahålls av den anropande komponenten korrekt kan skriva över standardvärden. Vissa klasser bör dock aldrig kunna skrivas över — till exempel strukturella klasser som bestämmer komponentens layout. Ett mönster är att tillämpa strukturella klasser utan sammanslagning och endast slå samman presentativa klasser med den indata som skickas av den anropande komponenten.

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}
    />
  );
}

Undvik dynamiska klassnamn

Tailwinds JIT-motor hittar klassnamn genom att skanna källfiler efter fullständiga strängar. Skapa aldrig klassnamn dynamiskt genom att konkatenera delar av strängar, som 'text-' + color + '-500'. JIT-skannern hittar inte dessa, och klasserna rensas bort från produktionspaketet. Använd alltid fullständiga klassnamn som strängar, även om det innebär ett längre villkorsuttryck.

// 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])} />

Memoisera klassberäkningar

När en komponent renderas ofta och har komplexa beräkningar av className kan du överväga att memoisera resultatet med useMemo. Operationerna clsx och twMerge är snabba, men i komponenter som renderas hundratals gånger per sekund (som objekt i en virtuell lista) gör även små besparingar skillnad. Oftare förbättrar det läsbarheten att flytta klassberäkningen till en variabel utanför JSX, oavsett prestanda.

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>;
}

Testa logik för villkorsstyrda klasser

Skriv enhetstester för komponenter med komplex logik för villkorsstyrda klasser för att förhindra regressioner. Med React Testing Library kan du kontrollera att specifika Tailwind-klasser finns eller saknas beroende på prop-värden. Detta är särskilt viktigt för klasser som har betydelse för tillgängligheten — om du testar att en inaktiverad knapp har cursor-not-allowed och opacity-50 får du större säkerhet i att gränssnittets visuella ledtrådar är korrekta.

// 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');
});

Snabb kontroll

Testa dina kunskaper om begreppen i Tailwind CSS Mastery från den här lektionen.

Sammanfattning av lektionen

I den här lektionen lärde du dig att clsx på ett säkert sätt bygger villkorsstyrda klasssträngar genom att filtrera bort falska värden, att tailwind-merge löser konflikter mellan Tailwind-verktygsklasser så att den senast tillämpade vinner och att hjälpfunktionen cn() kombinerar båda i ett enda anrop som används i hela React-projektet. Härnäst utforskar vi komponentvarianter med CVA för typade variant-API:er.

Gratis att börja

Lär dig HTML med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
30
Lektioner
120

Vanliga frågor

Är lektionen ”Villkorliga klasser i React” gratis?

Ja – hela texten till ”Villkorliga klasser i React” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Tailwind CSS Academy, kan Ni uppgradera till CoddyKit PRO. Kursen i Tailwind CSS Academy innehåller totalt 4 lektioner.

Vad lär jag mig i ”Villkorliga klasser i React”?

Använd clsx eller tailwind-merge för att villkorligt tillämpa och säkert slå samman Tailwind-klasser baserat på komponenters props och state, utan klasskonflikter. Ni övar på Tailwind CSS Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Tailwind CSS Academy?

Du behöver inga förkunskaper. Utbildningen i Tailwind CSS Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 2 av 4.

Hur lång tid tar lektionen ”Villkorliga klasser i React”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Tailwind CSS Academy-lektionen?

Ja. Varje Tailwind CSS Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Konfigurera Tailwind i Next.js
  2. Villkorliga klasser i React
  3. Komponentvarianter med CVA
  4. Undvik klasskonflikter med tailwind-merge
← Tillbaka till Tailwind CSS Academy