Tailwind
In Next.js you add Tailwind by installing three packages, creating tailwind.config.js, adding the @tailwind directives to globals.css, and importing that file in _app.tsx. Astro ships an official integration that does all of that in one command.
Playground note: Tailwind requires its Vite integration to be installed in the project — a single-file
.astropage in StackBlitz cannot load it without the full setup. This lesson is code-only. Copy any snippet to a local project created withnpx create astro@latestand run the steps below to try it live.
Adding Tailwind to Astro
Section titled “Adding Tailwind to Astro”# inside your Astro projectnpx astro add tailwindThe CLI installs tailwindcss and the official @tailwindcss/vite Vite plugin, wires it into astro.config.mjs under vite.plugins (Tailwind 4 is a Vite plugin, not an Astro integration — the old @astrojs/tailwind package is deprecated and only relevant to Tailwind 3 projects), and creates src/styles/global.css with the Tailwind import. Accept all prompts.
// astro.config.mjs — after running the commandimport { defineConfig } from 'astro/config';import tailwindcss from '@tailwindcss/vite';
export default defineConfig({ vite: { plugins: [tailwindcss()], },});/* src/styles/global.css — auto-generated */@import "tailwindcss";There is no tailwind.config.mjs and no content glob to maintain — Tailwind 4 scans your project automatically and is configured CSS-first (@theme { ... } in the CSS file for design-token overrides). Import the stylesheet once in a shared layout:
---import '../styles/global.css';---Using utility classes in .astro vs React
Section titled “Using utility classes in .astro vs React”// Card.tsx (Next.js + Tailwind)export default function Card({ title, description, featured = false,}: { title: string; description: string; featured?: boolean;}) { return ( <div className={`rounded-xl border p-6 shadow-sm ${ featured ? 'border-indigo-500 bg-indigo-50' : 'border-slate-200 bg-white' }`}> <h2 className="mb-2 text-lg font-semibold text-slate-900">{title}</h2> <p className="text-sm text-slate-600">{description}</p> </div> );}---// Card.astro (Astro + Tailwind)interface Props { title: string; description: string; featured?: boolean; }const { title, description, featured = false } = Astro.props;---<div class:list={[ "rounded-xl border p-6 shadow-sm", featured ? "border-indigo-500 bg-indigo-50" : "border-slate-200 bg-white",]}> <h2 class="mb-2 text-lg font-semibold text-slate-900">{title}</h2> <p class="text-sm text-slate-600">{description}</p></div>The only mandatory change: className becomes class. Everything else — utility classes, responsive prefixes, dark-mode variants, arbitrary values — works identically.
Mixing Tailwind with scoped styles
Section titled “Mixing Tailwind with scoped styles”You can use Tailwind utilities and a <style> block in the same .astro file. This is useful when a component needs a complex selector (e.g., nth-child, ::before) that Tailwind cannot express cleanly.
<section class="py-24 px-6 bg-gradient-to-br from-indigo-600 to-purple-700"> <h1 class="text-4xl font-bold text-white mb-4">Ship faster</h1> <p class="text-indigo-200 max-w-xl">Tailwind utilities + scoped styles in one component.</p></section>
<style> /* Scoped rule for a complex selector Tailwind cannot express */ section > *:not(:last-child) { margin-bottom: 1rem; }</style>Tailwind and framework components (React / Vue inside Astro)
Section titled “Tailwind and framework components (React / Vue inside Astro)”Utility classes also work on framework components rendered inside Astro pages. Pass classes as class on the Astro wrapper or as className inside JSX if you are inside a .tsx Island component.
---import Counter from './Counter.tsx';---<!-- Tailwind class on the Astro element wrapper --><div class="max-w-screen-lg mx-auto px-4"> <!-- className inside the React Island --> <Counter className="mt-8" client:visible /></div>