# Register Sources Only Where Detection Cannot Reach

**MUST** · **ID:** `performance-content-paths` · **Category:** performance
**Source:** [Tailwind](https://tailwindcss.com/docs)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/performance-content-paths

> MUST: Configure Tailwind content paths to include all files using utility classes

Tailwind v4 auto-detects sources — add @source only for files outside that sweep

There is no `content` array in Tailwind v4. It was removed. Tailwind now walks the project itself, starting from where the CSS is imported, skipping anything in .gitignore and every binary/asset extension — so in a normal app the correct configuration is none at all, and pasting a v3 `content: [...]` block does nothing but mislead the next reader. Reach for `@source "…"` in exactly one situation: a file that holds class names but that the sweep cannot see — a component library inside node_modules (which is gitignored), or a template directory outside the project root. `@source "../node_modules/@acme/ui/dist"`. Two escape hatches complete the picture: `@source not "…"` excludes a path the sweep found but should not scan (a huge generated fixture directory), and `@import "tailwindcss" source(none)` disables auto-detection entirely so you can list every source explicitly — worth it only when you genuinely need that control.

## Bad — do not do this

`performance-content-paths-bad`

```tsx
export function ContentPathsBad() {
  return (
    <div className="w-full max-w-md space-y-4">
      <div className="bg-card border border-border rounded-lg p-4">
        <h3 className="text-sm font-semibold mb-1">tailwind.config.js</h3>
        <p className="text-xs text-muted-foreground mb-3">Tailwind v4 — this file is not read at all</p>
        <div className="bg-muted rounded-md p-3 font-mono text-xs overflow-x-auto">
          <pre className="text-foreground">{`export default {
  content: [
    './src/**/*.{js,ts,jsx,tsx}',
    './components/**/*.tsx',
  ],
}`}</pre>
        </div>
        <div className="mt-4 space-y-2">
          <div className="flex items-start gap-2">
            <span className="mt-1.5 size-2 shrink-0 rounded-full bg-error" />
            <span className="text-sm">
              <code>content</code> was removed in v4 — this array is inert
            </span>
          </div>
          <div className="flex items-start gap-2">
            <span className="mt-1.5 size-2 shrink-0 rounded-full bg-error" />
            <span className="text-sm">
              The classes still build, so nobody notices the config does nothing
            </span>
          </div>
          <div className="flex items-start gap-2">
            <span className="mt-1.5 size-2 shrink-0 rounded-full bg-error" />
            <span className="text-sm">
              The one path it needed — a lib inside <code>node_modules</code> — is still missed
            </span>
          </div>
        </div>
      </div>
      <p className="text-xs text-destructive">
        Copying a v3 <code>content</code> array into a v4 project is cargo cult: it is never read, and it hides the fact that the real gap was never closed
      </p>
    </div>
  );
}
```

## Good — do this

`performance-content-paths-good`

```tsx
export function ContentPathsGood() {
  return (
    <div className="w-full max-w-md space-y-4">
      <div className="bg-card border border-border rounded-lg p-4">
        <h3 className="text-sm font-semibold mb-1">app.css</h3>
        <p className="text-xs text-muted-foreground mb-3">Tailwind v4 — sources are detected, not configured</p>
        <div className="bg-muted rounded-md p-3 font-mono text-xs overflow-x-auto">
          <pre className="text-foreground">{`@import "tailwindcss";

/* Only what auto-detection cannot see:
   node_modules is gitignored, so it is skipped */
@source "../node_modules/@acme/ui/dist";`}</pre>
        </div>
        <div className="mt-4 space-y-2">
          <div className="flex items-start gap-2">
            <span className="mt-1.5 size-2 shrink-0 rounded-full bg-success" />
            <span className="text-sm">
              Your own <code>src/</code> needs no configuration — Tailwind walks it
            </span>
          </div>
          <div className="flex items-start gap-2">
            <span className="mt-1.5 size-2 shrink-0 rounded-full bg-success" />
            <span className="text-sm">
              <code>.gitignore</code> and binary files are skipped automatically
            </span>
          </div>
          <div className="flex items-start gap-2">
            <span className="mt-1.5 size-2 shrink-0 rounded-full bg-success" />
            <span className="text-sm">
              <code>@source</code> registers the one blind spot: a gitignored UI package
            </span>
          </div>
        </div>
      </div>
      <p className="text-xs text-success">
        Escape hatches when you need them: <code>@source not "…"</code> excludes a scanned path, and{' '}
        <code>@import "tailwindcss" source(none)</code> turns detection off entirely
      </p>
    </div>
  );
}
```

## References

- [Tailwind — Detecting classes in source files](https://tailwindcss.com/docs/detecting-classes-in-source-files)
- [Tailwind — Functions and directives (@source)](https://tailwindcss.com/docs/functions-and-directives)
- [Tailwind — v4 upgrade guide](https://tailwindcss.com/docs/upgrade-guide)
