# Class Precedence

**SHOULD** · **ID:** `interactions-class-precedence` · **Category:** interactions
**Source:** [Tailwind](https://tailwindcss.com/docs)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/interactions-class-precedence

> SHOULD: Use tailwind-merge (via cn()) to handle class conflicts in dynamic composition

Understand Tailwind's class order and override patterns

When combining classes dynamically, the last class in CSS source order wins (not the last class you write). Use tailwind-merge to intelligently merge Tailwind classes and resolve conflicts.

## Bad — do not do this

`interactions-class-precedence-bad`

```tsx
interface ButtonProps {
  variant?: 'default' | 'large';
  className?: string;
}

function StringConcatButton({ variant = 'default', className }: ButtonProps) {
  const baseClasses = 'px-4 py-2 bg-primary text-primary-foreground rounded-lg text-sm';
  const variantClasses = variant === 'large' ? 'px-6 py-3 text-base' : '';

  // This can cause conflicts!
  return (
    <button className={`${baseClasses} ${variantClasses} ${className || ''}`}>
      {variant === 'large' ? 'Large Button' : 'Default'}
    </button>
  );
}

export function ClassPrecedenceBad() {
  return (
    <div className="w-full max-w-sm space-y-4">
      <div className="bg-card border border-border rounded-lg p-4">
        <h4 className="font-medium mb-3">String Concatenation</h4>
        <div className="flex flex-wrap items-center gap-3">
          <StringConcatButton />
          <StringConcatButton variant="large" />
          <StringConcatButton className="bg-secondary text-secondary-foreground" />
        </div>
        <div className="mt-4 bg-muted rounded p-3 font-mono text-xs">
          <code className="text-error">{`\`\${base} \${variant} \${className}\``}</code>
        </div>
        <p className="text-xs text-muted-foreground mt-2">
          px-4 and px-6 both exist - which wins depends on CSS source order
        </p>
      </div>
      <p className="text-xs text-error">
        String concatenation doesn't resolve class conflicts
      </p>
    </div>
  );
}
```

## Good — do this

`interactions-class-precedence-good`

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

interface ButtonProps {
  variant?: 'default' | 'large';
  className?: string;
}

function MergedButton({ variant = 'default', className }: ButtonProps) {
  return (
    <button
      className={cn(
        'px-4 py-2 bg-primary text-primary-foreground rounded-lg text-sm',
        variant === 'large' && 'px-6 py-3 text-base',
        className
      )}
    >
      {variant === 'large' ? 'Large Button' : 'Default'}
    </button>
  );
}

export function ClassPrecedenceGood() {
  return (
    <div className="w-full max-w-sm space-y-4">
      <div className="bg-card border border-border rounded-lg p-4">
        <h4 className="font-medium mb-3">Using cn() / tailwind-merge</h4>
        <div className="flex flex-wrap items-center gap-3">
          <MergedButton />
          <MergedButton variant="large" />
          <MergedButton className="bg-secondary text-secondary-foreground" />
        </div>
        <div className="mt-4 bg-muted rounded p-3 font-mono text-xs">
          <code>{`cn('px-4', variant && 'px-6', className)`}</code>
        </div>
      </div>
      <p className="text-xs text-success">
        tailwind-merge intelligently resolves conflicting utilities
      </p>
    </div>
  );
}
```

## References

- [tailwind-merge](https://github.com/dcastil/tailwind-merge)
- [clsx](https://github.com/lukeed/clsx)
