Tailwind CSS Academy · leksjon

Unngå klassekonflikter med tailwind-merge

Forstå hvordan spesifisitet for Tailwind-klasser fungerer, og bruk tailwind-merge for å sikre at den sist anvendte varianten vinner uten spesifisitetsfeil.

Leksjon 4 av 413 trinn

Unngå klassekonflikter med tailwind-merge er en gratis leksjon i Tailwind CSS Academy på CoddyKit. Dette er leksjon 4 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Tailwind CSS Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Tailwind CSS Academy inneholder totalt 4 leksjoner.

Slik oppstår Tailwind-klassekonflikter

Tailwind-verktøyklasser angir individuelle CSS-egenskaper. Når to klasser målretter den samme egenskapen — for eksempel p-4 og p-8, eller text-blue-500 og text-red-500 — ender begge opp i elementets klasseliste. Nettleseren løser konflikten ved hjelp av CSS-kaskaderekkefølgen: verktøyklassen som ble generert sist i Tailwind-stilarket, vinner, uavhengig av rekkefølgen i HTML-en. Uten tailwind-merge blir det derfor uforutsigbart å overstyre stiler fra overordnede komponenter.

<!-- Both p-4 and p-8 appear in the DOM -->
<div class="p-4 p-8">...</div>
<!-- Which padding is applied? Depends on Tailwind's
     stylesheet order, not the class string order. -->

<!-- Same problem with text colors -->
<div class="text-gray-900 text-blue-500">...</div>
<!-- Will the text be gray or blue? You cannot be sure. -->

Hva tailwind-merge gjør

tailwind-merge er et verktøy som kjøres ved kjøring, og som analyserer en klassestreng og fjerner motstridende Tailwind-klasser. Bare den siste klassen fra hver konfliktgruppe beholdes. Verktøyet har en intern oversikt over hvilke Tailwind-verktøyklasser som er i konflikt med hverandre — for eksempel at p-4 og p-8 begge angir padding, eller at font-bold og font-medium begge angir font-weight. Den siste klassen i inndatastrengen vinner alltid.

import { twMerge } from 'tailwind-merge';

// Conflict resolution: last value wins
twMerge('p-4 p-8')
// Output: 'p-8'

twMerge('text-gray-900 text-blue-500')
// Output: 'text-blue-500'

twMerge('font-bold font-medium text-sm text-lg')
// Output: 'font-medium text-lg'

// Non-conflicting classes are kept
twMerge('flex items-center gap-4 p-4')
// Output: 'flex items-center gap-4 p-4'

Installering og grunnleggende bruk

Installer tailwind-merge som en produksjonsavhengighet (ikke bare for utvikling, siden den kjører ved kjøring). Importer twMerge og pakk inn alle klassestrenger som kan inneholde konflikter. Funksjonen tar imot flere argumenter og slår dem sammen, på samme måte som clsx tar imot flere argumenter — noe som gjør det enkelt å ta den i bruk i eksisterende kode.

npm install tailwind-merge

import { twMerge } from 'tailwind-merge';

// Single string
const cls = twMerge('bg-blue-500 bg-red-500');
// → 'bg-red-500'

// Multiple arguments (like clsx)
const cls2 = twMerge(
  'px-4 py-2 rounded',   // base
  'px-8',                // override padding-x
  'text-white'
);
// → 'py-2 rounded px-8 text-white'

twMerge forstår verktøygrupper

tailwind-merge forstår hele Tailwinds taksonomi for verktøyklasser. Den vet at px-4 angir horisontal padding, mens py-2 angir vertikal padding, og at de derfor ikke er i konflikt. Den vet at shadow-md og shadow-lg begge angir egenskapen box-shadow, og derfor er i konflikt. Den håndterer også varianter som hover:bg-blue-500 og hover:bg-red-500 som en separat konfliktgruppe fra de tilsvarende klassene uten variant.

import { twMerge } from 'tailwind-merge';

// px and py don't conflict with each other
twMerge('px-4 py-2 px-8')
// → 'py-2 px-8'  (px-4 removed, px-8 wins)

// Hover variants are separate groups
twMerge('hover:bg-blue-500 hover:bg-red-500 bg-white')
// → 'bg-white hover:bg-red-500'

// Shadow variants
twMerge('shadow-sm shadow-lg shadow-md')
// → 'shadow-md'

// Responsive prefixes are separate groups
twMerge('md:text-xl md:text-2xl text-sm')
// → 'text-sm md:text-2xl'

Mønsteret med cn()-hjelpefunksjonen

Det vanlige mønsteret i React-/Next.js-prosjekter er å kombinere clsx og twMerge i én cn()-hjelpefunksjon. clsx håndterer betinget klasselogikk og filtrerer bort falske verdier, mens twMerge deretter løser eventuelle konflikter i den resulterende strengen. Definer dette én gang i utils-filen, og bruk det overalt — dette er fremgangsmåten som brukes av shadcn/ui og de fleste moderne Tailwind-komponentbiblioteker.

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

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

// Usage: conditional classes with conflict resolution
function Badge({ active, className }) {
  return (
    <span
      className={cn(
        'px-2 py-1 rounded-full text-sm font-medium',
        active ? 'bg-blue-100 text-blue-800' : 'bg-gray-100 text-gray-600',
        className  // caller override — twMerge resolves conflicts
      )}
    >
      Label
    </span>
  );
}

Mønsteret for overstyring med className-prop

Det viktigste bruksområdet for tailwind-merge i React-komponenter er å muliggjøre sikre overstyringer med className-propen. Når en komponent har standardstiler og den som kaller komponenten, oppgir ekstra eller erstattende klasser via className-propen, sørger twMerge for at hensikten til den som kaller komponenten, blir respektert. Dermed blir komponentene reelt tilpasningsbare uten at brukerne må kjempe mot CSS-spesifisitet eller bruke !important.

// Without twMerge: caller override might not work
function Card({ className, children }) {
  return (
    <div className={'bg-white rounded-xl p-6 shadow ' + className}>
      {children}
    </div>
  );
}
<Card className='bg-gray-50' /> // bg-white and bg-gray-50 both present!

// With twMerge: caller override always wins
function Card({ className, children }) {
  return (
    <div className={cn('bg-white rounded-xl p-6 shadow', className)}>
      {children}
    </div>
  );
}
<Card className='bg-gray-50' /> // only bg-gray-50 (bg-white removed)

Arbitrære verdier og twMerge

tailwind-merge håndterer også klasser med arbitrære verdier ved hjelp av Tailwinds klammeparentesnotasjon. Den identifiserer korrekt at p-[10px] er i konflikt med p-4, og at w-[300px] er i konflikt med w-full. Dette er viktig fordi arbitrære verdier er vanlige når man utformer komponenter som trenger nøyaktige mål som ikke finnes i standardskalaen.

import { twMerge } from 'tailwind-merge';

// Arbitrary values conflict with standard utilities
twMerge('p-4 p-[10px]')
// → 'p-[10px]'  (last wins)

twMerge('w-full w-[300px]')
// → 'w-[300px]'

twMerge('text-sm text-[15px]')
// → 'text-[15px]'

// Mix of standard and arbitrary
twMerge('bg-blue-500 bg-[#2563eb]')
// → 'bg-[#2563eb]'

Konfigurere twMerge for egendefinerte klasser

Som standard kjenner tailwind-merge bare til Tailwinds innebygde verktøyklasser. Hvis De har lagt til egendefinerte verktøyklasser via programtillegg eller konfigurasjonen, vet ikke twMerge at de er i konflikt med relaterte standardklasser. Bruk extendTailwindMerge() til å lære twMerge om de egendefinerte klassene, slik at konfliktløsningen fungerer korrekt for det utvidede settet med verktøyklasser.

import { extendTailwindMerge } from 'tailwind-merge';

// Tell twMerge about custom text-shadow utilities
const customTwMerge = extendTailwindMerge({
  extend: {
    classGroups: {
      'text-shadow': ['text-shadow-sm', 'text-shadow-md', 'text-shadow-lg', 'text-shadow-none']
    }
  }
});

// Now conflicts are resolved correctly
customTwMerge('text-shadow-sm text-shadow-lg')
// → 'text-shadow-lg'

// Update cn() to use the custom instance
export const cn = (...inputs) => customTwMerge(clsx(inputs));

Ytelseshensyn

tailwind-merge analyserer og løser klassestrenger ved kjøring i nettleseren. For de fleste applikasjoner går dette så raskt at det knapt merkes. Komponenter som gjengir tusenvis av instanser (for eksempel virtuelle lister eller tabellceller), kan imidlertid ha nytte av å bufre den løste klassestrengen. tailwind-merge tilbyr en createTailwindMerge()-fabrikk som muliggjør en egendefinert cache-adapter. For de fleste apper er standardbufferet, som internt bruker LRU, tilstrekkelig.

import { createTailwindMerge, getDefaultConfig } from 'tailwind-merge';

// Create a version with a larger cache for high-volume usage
const twMerge = createTailwindMerge(getDefaultConfig);

// For critical performance: memoize with React.useMemo
function VirtualListItem({ selected, disabled, className }) {
  const itemClass = useMemo(
    () => cn(
      'flex items-center px-3 py-2',
      selected && 'bg-blue-50',
      disabled && 'opacity-50',
      className
    ),
    [selected, disabled, className]
  );
  return <div className={itemClass}>{/* ... */}</div>;
}

Vanlige fallgruver med tailwind-merge

tailwind-merge har noen kanttilfeller De bør kjenne til. Den håndterer ikke CSS Modules-klasser — bare navn på Tailwind-verktøyklasser. Hvis en klassestreng inneholder klasser som ikke er fra Tailwind (for eksempel egendefinerte BEM-klasser), lar twMerge dem passere uendret. Enkelte verktøyklasser som genereres av programtillegg, blir heller ikke gjenkjent uten at sammenslåingskonfigurasjonen utvides. Til slutt opererer twMerge på klassestrengen, ikke på den gjengitte CSS-en, så den kan ikke løse konflikter som skyldes CSS-kaskaden utover Tailwind-verktøyklasser.

import { twMerge } from 'tailwind-merge';

// Non-Tailwind classes pass through untouched
twMerge('card__header bg-white bg-gray-50')
// → 'card__header bg-gray-50'  (custom class kept, Tailwind conflict resolved)

// CSS Modules classes (hash-based) also pass through
twMerge('styles__button_abc123 bg-blue-500 bg-red-500')
// → 'styles__button_abc123 bg-red-500'

// Unknown plugin classes (without extendTailwindMerge) pass through
twMerge('text-shadow-lg text-shadow-sm')  // both kept if not configured
// → 'text-shadow-lg text-shadow-sm' (no conflict detected)

Teste med tailwind-merge

Når De skriver enhetstester for komponenter som bruker cn(), bør De teste det faktiske resultatet av klassestrengen for å kontrollere at konflikter løses korrekt. Dette er særlig verdifullt for komponentbiblioteker, der brukerne må kunne overstyre standardverdier på en pålitelig måte. Snapshot-tester av className-resultater sikrer at fremtidige omstruktureringer ikke utilsiktet ødelegger overstyringsfunksjonen.

// card.test.ts
import { cn } from '@/lib/utils';
import { cardVariants } from './Card';

test('caller className overrides default bg', () => {
  const result = cn(cardVariants({ variant: 'white' }), 'bg-gray-50');
  // bg-white from variant should be removed, bg-gray-50 should win
  expect(result).not.toContain('bg-white');
  expect(result).toContain('bg-gray-50');
});

test('non-conflicting classes are additive', () => {
  const result = cn('p-6 rounded-xl', 'mt-4');
  expect(result).toContain('p-6');
  expect(result).toContain('rounded-xl');
  expect(result).toContain('mt-4');
});

Kort kontroll

Test forståelsen Deres av konseptene fra Tailwind CSS Mastery i denne leksjonen.

Oppsummering av leksjonen

I denne leksjonen lærte De at tailwind-merge løser konflikter mellom Tailwind-verktøyklasser ved å beholde bare den siste klassen fra hver konfliktgruppe, at cn()-hjelpefunksjonen kombinerer clsx og twMerge for betinget og konfliktfri klassehåndtering, og at extendTailwindMerge() lærer biblioteket om egendefinerte programtilleggsklasser. Deretter skal vi se på Headless UI og hvordan det integreres med Tailwind for tilgjengelige komponenter.

Gratis å komme i gang

Lær deg HTML med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
30
Leksjoner
120

Ofte stilte spørsmål

Er leksjonen «Unngå klassekonflikter med tailwind-merge» gratis?

Ja – hele teksten i «Unngå klassekonflikter med tailwind-merge» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av Tailwind CSS Academy-kurset, kan du oppgradere til CoddyKit PRO. Kurset i Tailwind CSS Academy inneholder totalt 4 leksjoner.

Hva lærer jeg i «Unngå klassekonflikter med tailwind-merge»?

Forstå hvordan spesifisitet for Tailwind-klasser fungerer, og bruk tailwind-merge for å sikre at den sist anvendte varianten vinner uten spesifisitetsfeil. Du øver på Tailwind CSS Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med Tailwind CSS Academy?

Ingen tidligere erfaring er nødvendig. Tailwind CSS Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 4 av 4.

Hvor lang tid tar leksjonen «Unngå klassekonflikter med tailwind-merge»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne Tailwind CSS Academy-leksjonen?

Ja. Alle Tailwind CSS Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Sette opp Tailwind i Next.js
  2. Betingede klasser i React
  3. Komponentvarianter med CVA
  4. Unngå klassekonflikter med tailwind-merge
← Tilbake til Tailwind CSS Academy