Skip to content
Angular ng libraries 4 min read

Tailwind CSS with Angular

Tailwind CSS is a utility-first framework that lets you compose designs directly in your templates with small, single-purpose classes like flex, p-4, and text-sm. Instead of writing custom CSS files and inventing class names, you assemble styles inline, which keeps markup and presentation together and dramatically reduces context switching. Angular’s build pipeline integrates Tailwind cleanly through PostCSS, so the same JIT engine that scans your .html and .ts files generates only the CSS you actually use. This page walks through installing Tailwind, wiring it into Angular’s build, and applying it idiomatically to standalone components.

Installing Tailwind in an Angular project

Tailwind is delivered as a PostCSS plugin. Angular’s CLI already runs PostCSS during builds, so you only need to add the packages and provide a configuration file. Install Tailwind along with its peer dependencies.

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init

The init command scaffolds a tailwind.config.js at the project root. The most important field is content, which tells Tailwind’s engine which files to scan for class names so it can tree-shake unused utilities out of the final bundle.

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./src/**/*.{html,ts}'],
  theme: {
    extend: {
      colors: {
        brand: '#4f46e5',
      },
    },
  },
  plugins: [],
};

Include ts in the content glob. Angular components often build class strings dynamically in TypeScript (for example with ngClass bindings), and omitting .ts causes those utilities to be purged from production builds.

Registering the PostCSS plugin

Angular detects a .postcssrc.json (or postcss.config.js) at the workspace root and feeds it into its bundler. Create the file so the Tailwind and Autoprefixer plugins run on every stylesheet.

{
  "plugins": {
    "tailwindcss": {},
    "autoprefixer": {}
  }
}

Next, add Tailwind’s three layer directives to your global stylesheet, typically src/styles.css (or styles.scss). These directives are replaced at build time with the generated base, component, and utility styles.

@tailwind base;
@tailwind components;
@tailwind utilities;

That is the entire wiring. Run the dev server and Tailwind classes become available everywhere.

ng serve

Using utilities in standalone components

With Tailwind active, you style templates by composing utility classes. Because modern Angular favors standalone components, no module wiring is required — utilities resolve globally from the compiled stylesheet.

import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-counter',
  standalone: true,
  template: `
    <div class="mx-auto max-w-sm rounded-xl bg-white p-6 shadow-md">
      <h2 class="text-lg font-semibold text-gray-800">Counter</h2>
      <p class="mt-2 text-3xl font-bold text-brand">{{ count() }}</p>
      <button
        (click)="increment()"
        class="mt-4 rounded-lg bg-brand px-4 py-2 text-white hover:bg-indigo-700"
      >
        Increment
      </button>
    </div>
  `,
})
export class CounterComponent {
  count = signal(0);
  increment(): void {
    this.count.update((n) => n + 1);
  }
}

Note the use of the custom brand color defined in tailwind.config.js, which works exactly like a built-in utility.

Conditional and dynamic classes

Combine Tailwind with Angular’s bindings to toggle styling reactively. The ngClass directive accepts an object whose keys are class strings and whose values are booleans, while signals drive the condition.

import { Component, signal } from '@angular/core';
import { NgClass } from '@angular/common';

@Component({
  selector: 'app-status',
  standalone: true,
  imports: [NgClass],
  template: `
    <span
      class="rounded-full px-3 py-1 text-sm font-medium"
      [ngClass]="{
        'bg-green-100 text-green-800': active(),
        'bg-gray-100 text-gray-500': !active()
      }"
    >
      @if (active()) {
        Online
      } @else {
        Offline
      }
    </span>
    <button class="ml-2 underline" (click)="toggle()">Toggle</button>
  `,
})
export class StatusComponent {
  active = signal(true);
  toggle(): void {
    this.active.update((v) => !v);
  }
}

Verifying the build output

A production build runs Tailwind in purge mode, emitting only the utilities you referenced. You can confirm the generated CSS stays small.

ng build --configuration production

Output:

Initial chunk files   | Names         |  Raw size | Estimated transfer size
styles-A1B2C3D4.css   | styles        |  12.41 kB |                 3.18 kB
main-E5F6G7H8.js      | main          | 142.06 kB |                42.90 kB

Application bundle generation complete. [4.812 seconds]

Configuration reference

ConcernWhere it livesPurpose
Files to scancontent in tailwind.config.jsDetermines which classes survive purging
PostCSS plugins.postcssrc.jsonHooks Tailwind into Angular’s bundler
Layer directivessrc/styles.cssInjects base/component/utility CSS
Theme tokenstheme.extendCustom colors, spacing, fonts
Dark modedarkMode: 'class'Enables dark: variants via a class

If utilities appear in development but vanish in production, the cause is almost always a missing path in the content glob. Tailwind only emits classes it can statically find in the scanned files.

Best practices

  • Keep the content glob tight but complete (./src/**/*.{html,ts}) so purging stays accurate without scanning node_modules.
  • Define repeated design tokens (colors, spacing, fonts) under theme.extend rather than hardcoding hex values throughout templates.
  • For recurring multi-class patterns, extract a reusable Angular component instead of copy-pasting long class strings, keeping markup DRY.
  • Avoid building Tailwind class names with string concatenation that the scanner cannot see; bind complete class strings via ngClass so they survive purging.
  • Enable darkMode: 'class' and toggle a class on the root element to support theming with signals.
  • Run a production build periodically to confirm the emitted CSS bundle stays lean and that no utilities are unexpectedly purged.
Last updated June 14, 2026
Was this helpful?