Betingede klasser i React
Brug clsx eller tailwind-merge til betinget at anvende og sikkert kombinere Tailwind-klasser baseret på komponenters props og state uden klassekonflikter.
Betingede klasser i React er en gratis Tailwind CSS Academy-lektion på CoddyKit. Dette er lektion 2 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i Tailwind CSS Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Tailwind CSS Academy-kurset indeholder 4 lektioner i alt.
Problemet med strengsammenkædning
Det er i første omgang enkelt at anvende Tailwind-klasser betinget i React, men naiv strengsammenkædning bliver hurtigt fejlbehæftet. Når du sammenkæder strenge med skabelonstrenge, kan undefined eller false ved et uheld blive inkluderet i className, hvilket fører til ugyldige klassenavne i DOM'en. Endnu vigtigere er det, at modstridende Tailwind-hjælpeklasser — som text-blue-500 og text-red-500 — ikke ophæver hinanden; rækkefølgen i typografiarket afgør, hvilken der vinder, ikke rækkefølgen i din className-streng.
// 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>
);
}Brug af clsx til betingede klasser
clsx er et lille hjælpeværktøj, der sikkert opbygger className-strenge ud fra betingelser, objekter og arrays. Det filtrerer falsy-værdier som false, null og undefined fra, så din DOM altid får rene klassenavne. Du kan sende strenge, objekter med booleske værdier eller arrays af begge dele. Installér det med npm install clsx, og importér det overalt, hvor du har brug for logik til betingede 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-objektsyntaks
clsx accepterer en objektsyntaks, hvor nøglerne er klassenavne, og værdierne er booleske betingelser. Det er især let at læse, når du har mange betingede klasser, der er grupperet efter funktion. Du kan blande objektsyntaksen med strengargumenter i samme clsx-kald, så det er nemt at adskille ubetingede basisklasser fra betingede 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 klassekonflikter
Selv når clsx producerer rene klassestrenge, er konflikter mellem Tailwind-klasser stadig et problem. Hvis en forælder anvender text-blue-500, og et underordnet element eller en tilsidesættelse anvender text-red-500, vises begge klasser i DOM'en. CSS' kaskaderækkefølge (ikke DOM-rækkefølgen) afgør, hvilken der vinder — det er den, Tailwind senest genererede i typografiarket. Det gør tilsidesættelser uforudsigelige, især når du sender className-egenskaber fra kaldende kode.
// 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 intenttailwind-merge løser konflikter
tailwind-merge (twMerge) kender Tailwinds grupper af hjælpeklasser og sikrer, at den seneste værdi for enhver modstridende egenskab vinder. Den forstår, at bg-blue-500 og bg-red-500 begge angiver background-color, så den beholder kun den seneste. Installér den med npm install tailwind-merge, og pak dine clsx-kald ind i twMerge for at få forudsigelig adfærd ved tilsidesættelser.
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)Hjælpermønstret cn()
I de fleste Next.js- og React-projekter ser du en hjælpefunktion kaldet cn(), som kombinerer clsx og tailwind-merge i ét praktisk kald. Mønstret er så almindeligt, at create-next-app med shadcn/ui inkluderer det som standard. Definér den én gang i en hjælpefil, og importér den overalt, hvor du har brug for håndtering af betingede klasser uden konflikter.
// 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>
);
}Betingede klasser baseret på tilstand
Hjælpefunktionen cn() er særligt nyttig, når en komponents tilstand styrer visuelle ændringer. En kontaktafbryder, et aktivt navigationselement eller et formularfelt med valideringsfejl har alle brug for klasser, der ændres ud fra JavaScript-tilstanden. Når du bruger cn med objektsyntaks, bliver sammenhængen mellem tilstand og typografi straks tydelig — alle, der læser komponenten, kan se præcis, hvilke klasser der anvendes under de forskellige betingelser.
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>
);
}Sikker overførsel af className-egenskaber
En vigtig designbeslutning for genbrugelige komponenter er, hvordan du håndterer egenskaben className. Brug af twMerge sikrer, at klasser fra den kaldende kode korrekt tilsidesætter standarderne. Nogle klasser bør dog aldrig kunne tilsidesættes — f.eks. strukturelle klasser, der definerer komponentens layout. Et mønster er at anvende strukturelle klasser uden sammenfletning og kun sammenflette visuelle klasser med inputtet fra den kaldende kode.
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}
/>
);
}Undgå dynamiske klassenavne
Tailwinds JIT-motor finder klassenavne ved at scanne kildefiler som komplette strenge. Opbyg aldrig klassenavne dynamisk ved at sammenkæde delvise strenge som 'text-' + color + '-500'. JIT-scanneren finder ikke disse klasser, og de bliver fjernet fra produktionspakken. Brug altid komplette klassenavnsstrenge, også selvom det betyder et længere betinget udtryk.
// 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])} />Huskeberegning af klasseberegninger
Når en komponent gengives ofte og har komplekse className-beregninger, kan du overveje at gemme resultatet med useMemo. clsx- og twMerge-operationerne er hurtige, men selv små besparelser løber op i komponenter, der gengives hundredvis af gange i sekundet (som elementer i en virtuel liste). Oftere forbedrer det læsbarheden, uanset ydeevnen, at udtrække klasseberegningen til en variabel uden for JSX.
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>;
}Test af logik for betingede klasser
Skriv enhedstest for komponenter med kompleks logik for betingede klasser for at forhindre regressioner. Med React Testing Library kan du kontrollere, at bestemte Tailwind-klasser er til stede eller fraværende ud fra egenskabernes værdier. Det er især vigtigt for klasser, der har betydning for tilgængeligheden — hvis du tester, at en deaktiveret knap har cursor-not-allowed og opacity-50, får du sikkerhed for, at brugergrænsefladens signaler er korrekte.
// 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');
});Hurtigt tjek
Test din forståelse af begreberne fra Tailwind CSS Mastery i denne lektion.
Opsummering af lektionen
I denne lektion lærte du, at clsx sikkert opbygger betingede klassestrenge ved at filtrere falsy-værdier fra, at tailwind-merge løser konflikter mellem Tailwind-hjælpeklasser, så den senest anvendte vinder, og at cn()-hjælpefunktionen kombinerer begge dele i ét kald, som bruges i hele dit React-projekt. Næste gang ser vi på komponentvarianter med CVA til typebestemte variant-API'er.
Lær HTML med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 30
- Lektioner
- 120
Ofte stillede spørgsmål
Er lektionen “Betingede klasser i React” gratis?
Ja — hele teksten til “Betingede klasser i React” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af Tailwind CSS Academy-kurset, skal du opgradere til CoddyKit PRO. Tailwind CSS Academy-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Betingede klasser i React”?
Brug clsx eller tailwind-merge til betinget at anvende og sikkert kombinere Tailwind-klasser baseret på komponenters props og state uden klassekonflikter. Du øver dig i Tailwind CSS Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Tailwind CSS Academy?
Der kræves ingen tidligere erfaring. Tailwind CSS Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 4.
Hvor lang tid tager lektionen “Betingede klasser i React”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Tailwind CSS Academy-lektion?
Ja. Alle Tailwind CSS Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Opsætning af Tailwind i Next.js
- Betingede klasser i React
- Komponentvarianter med CVA
- Undgå klassekonflikter med tailwind-merge