Integrating Tailwind CSS with Next.js for Absolute Minimum CSS Payloads
A practical guide to building faster Next.js applications by eliminating unnecessary CSS without sacrificing developer experience. Learn how modern Tailwind CSS, production builds, and component architecture work together to keep your stylesheets incredibly lightweight.
One thing I have learned while building production Next.js applications is that CSS rarely gets the attention it deserves during performance optimization.
We spend hours optimizing images, reducing JavaScript bundles, implementing lazy loading, and improving server-side rendering. Meanwhile, our stylesheets quietly grow with every new component, utility class, and third-party dependency.
The problem becomes more noticeable as an application grows. A few pages turn into dozens, shared components accumulate styling variations, and suddenly the CSS payload is much larger than expected.
This is where Tailwind CSS becomes particularly useful.
When integrated correctly with Next.js, Tailwind CSS can produce extremely small production stylesheets. However, simply installing Tailwind does not guarantee minimal CSS. Your configuration, component architecture, content detection, and build process all matter.
Let’s look at how to approach this in a real production environment.
1. Start With the Modern Next.js and Tailwind Setup
For new projects, Next.js App Router combined with Tailwind CSS v4 provides a straightforward setup.
Create a Next.js application:
bash
npx create-next-app@latest minimal-css-app
cd minimal-css-appInstall Tailwind CSS:
bash
npm install tailwindcss @tailwindcss/postcssConfigure PostCSS in postcss.config.mjs:
js
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;Import Tailwind in your global stylesheet:
css
/* app/globals.css */
@import "tailwindcss";Then import the stylesheet from your root layout:
tsx
// app/layout.tsx
import "./globals.css";
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}That’s essentially the foundation.
Tailwind CSS v4 automatically discovers relevant source files in supported project structures and generates the CSS required by the utilities it finds.
There is no need to maintain a traditional Tailwind content array for a typical setup.
2. Understand Why Tailwind Produces Smaller CSS
The biggest advantage of Tailwind is its utility-based generation model.
Traditional CSS often encourages writing styles for individual components, even when many declarations are repeated.
For example:
css
.primary-button {
background: blue;
color: white;
padding: 12px 20px;
border-radius: 8px;
}
.secondary-button {
background: gray;
color: white;
padding: 12px 20px;
border-radius: 8px;
}With Tailwind:
tsx
<button className="rounded-lg bg-blue-600 px-5 py-3 text-white">
Submit
</button>
<button className="rounded-lg bg-gray-600 px-5 py-3 text-white">
Cancel
</button>Tailwind generates the required utility rules and reuses them wherever those utilities appear.
Using the same utility classes across multiple components does not generate a separate CSS declaration for every component.
This makes CSS output much more predictable as applications grow.
3. Avoid Dynamically Constructing Tailwind Classes
This is one of the most common mistakes I see in React applications.
Consider this example:
Get Meet’s stories in your inbox
Join Medium for free to get updates from this writer.
tsx
function Button({ color }: { color: string }) {
return (
<button className={`bg-${color}-600 text-white px-4 py-2`}>
Submit
</button>
);
}It looks perfectly reasonable from a React perspective.
However, Tailwind cannot reliably detect dynamically constructed class names because the complete utility names do not exist as recognizable strings in your source files.
The generated CSS might be missing the expected background colors.
A better approach is to use explicit class mappings:
tsx
const buttonVariants = {
primary: "bg-blue-600 hover:bg-blue-700",
danger: "bg-red-600 hover:bg-red-700",
success: "bg-green-600 hover:bg-green-700",
} as const;
type ButtonVariant = keyof typeof buttonVariants;
function Button({
variant = "primary",
}: {
variant?: ButtonVariant;
}) {
return (
<button
className={`rounded-lg px-4 py-2 text-white ${buttonVariants[variant]}`}
>
Submit
</button>
);
}Now Tailwind can identify every possible utility during source scanning.
This approach also makes component variants easier to maintain.
4. Keep Global CSS Extremely Small
One advantage of Tailwind is that you rarely need a large global stylesheet.
Avoid turning globals.css into a dumping ground for component-specific styles.
A reasonable starting point looks like this:
css
@import "tailwindcss";
:root {
color-scheme: light;
}
html {
scroll-behavior: smooth;
}
body {
min-height: 100vh;
}Keep reusable component styling inside component classes rather than continuously introducing new global selectors.
If you need custom reusable styles, Tailwind v4 also supports custom utilities through CSS-first configuration.
The goal is not to eliminate every custom CSS declaration. It is to avoid maintaining unnecessary styling rules.
5. Measure the Actual Production CSS Payload
This is where optimization becomes interesting.
A development server is not a reliable representation of your production CSS output.
Always inspect a production build:
bash
npm run buildNext.js generates production assets inside the .next directory.
You can inspect generated CSS files with:
bash
find .next/static/css -type fFor file sizes:
bash
du -sh .next/static/cssFor more detailed analysis, use browser DevTools and inspect the Network tab.
Pay attention to:
• Total transferred CSS size.
• Compressed size using Brotli or gzip.
• CSS files loaded on individual routes.
• Unnecessary styles introduced by third-party packages.
Remember that the generated CSS file size and the amount of CSS actually used by a browser are different measurements.
Both matter when optimizing frontend performance.
6. Be Careful With Third-Party Components
Tailwind cannot automatically remove unused CSS from every external library.
For example, adding a component library might introduce additional stylesheets that contribute significantly to your total CSS payload.
Before installing a UI library, consider whether you actually need its entire styling system.
For production applications, I prefer keeping the styling architecture predictable. Tailwind handles most application styling, while specialized libraries are introduced only when they provide meaningful functionality.
This reduces unnecessary dependencies and makes performance investigations easier.









