Sitemap
Meet

Meet

I build with React/Next.js and write about how. Get solutions to real-world frontend challenges: simplify code, master state, and ship faster.

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.

4 min read1 day ago

--

Press enter or click to view image in full size
Integrating Tailwind CSS with Next.js for Absolute Minimum CSS Payloads

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-app

Install Tailwind CSS:

bash

npm install tailwindcss @tailwindcss/postcss

Configure 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 build

Next.js generates production assets inside the .next directory.

You can inspect generated CSS files with:

bash

find .next/static/css -type f

For file sizes:

bash

du -sh .next/static/css

For 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.

--

--

Meet
Meet

I build with React/Next.js and write about how. Get solutions to real-world frontend challenges: simplify code, master state, and ship faster.