Skip to content

Class utilities

tailwindcss-obfuscator automatically detects classes inside the popular class-utility helpers.

Supported helpers

FunctionPackageSupport
cn()shadcn/uiFull
clsx()clsxFull
classnames()classnamesFull
classNames()classnamesFull
twMerge()tailwind-mergeFull
cva()class-variance-authorityFull
tv()tailwind-variantsFull

Examples

cn() — shadcn/ui

tsx
import { cn } from "@/lib/utils";

// All these classes will be obfuscated
<div className={cn("flex items-center", isActive && "bg-blue-500", disabled ? "opacity-50" : "opacity-100")}>;

clsx()

tsx
import clsx from "clsx";

<button
  className={clsx("px-4 py-2 rounded", {
    "bg-blue-500 text-white": variant === "primary",
    "bg-gray-200 text-gray-800": variant === "secondary",
  })}
>;

cva() — class-variance-authority

tsx
import { cva } from "class-variance-authority";

const buttonVariants = cva(
  // Base classes — obfuscated
  "inline-flex items-center justify-center rounded-md font-medium",
  {
    variants: {
      variant: {
        // All these classes will be obfuscated
        default: "bg-primary text-primary-foreground hover:bg-primary/90",
        destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
        outline: "border border-input bg-background hover:bg-accent",
      },
      size: {
        default: "h-10 px-4 py-2",
        sm: "h-9 rounded-md px-3",
        lg: "h-11 rounded-md px-8",
      },
    },
  }
);

twMerge()

tsx
import { twMerge } from "tailwind-merge";

// Classes are merged, then obfuscated
<div
  className={twMerge(
    "px-4 py-2 bg-blue-500",
    "px-6", // overrides px-4
  )}
>;

tv() — tailwind-variants

tsx
import { tv } from "tailwind-variants";

const button = tv({
  base: "font-medium rounded-full active:opacity-80",
  variants: {
    color: {
      primary: "bg-blue-500 text-white",
      secondary: "bg-purple-500 text-white",
    },
    size: {
      sm: "text-sm px-4 py-1",
      md: "text-base px-6 py-2",
    },
  },
});

Template literals

Template literals are supported too:

tsx
// Supported
<div className={`flex ${isActive ? 'bg-blue-500' : 'bg-gray-500'}`}>

// Supported inside cn()
<div className={cn(`
  flex
  items-center
  ${size === 'lg' ? 'p-6' : 'p-4'}
`)}>

Framework-specific patterns

Vue — :class binding

vue
<!-- Object syntax -->
<div :class="{ 'bg-blue-500': isActive, 'text-white': true }">

<!-- Array syntax -->
<div :class="['flex', isActive ? 'bg-blue-500' : 'bg-gray-500']">

<!-- Mixed syntax -->
<div :class="[baseClass, { 'bg-blue-500': isActive }]">

<!-- v-bind:class -->
<div v-bind:class="{ 'hover:bg-blue-600': true }">

Svelte — class: directive

svelte
<!-- class: directive -->
<div class:bg-red-500={hasError} class:flex>Content</div>

<!-- class:list directive -->
<div class:list={["flex", "items-center", { "bg-red-500": isActive }]}>

Astro — class:list directive

astro
---
const isActive = true;
---
<div class:list={["flex", "items-center", { "bg-red-500": isActive }]}>
  Content
</div>

Qwik — class$ binding

tsx
// Qwik reactive class binding
<div class$={"flex items-center bg-blue-500"}>Content</div>

Limitations

Dynamic classes are not supported

tsx
// ❌ DOES NOT WORK — class assembled at runtime
const color = 'blue'
<div className={`bg-${color}-500`}>

// ✅ WORKS — complete static class strings
<div className={color === 'blue' ? 'bg-blue-500' : 'bg-red-500'}>

All variants must be static

tsx
// ❌ DOES NOT WORK
const sizes = ["sm", "md", "lg"];
sizes.map((s) => `text-${s}`);

// ✅ WORKS
const sizeClasses = {
  sm: "text-sm",
  md: "text-base",
  lg: "text-lg",
};