Next.js supports several styling approaches out of the box; most real projects combine two or three of them.
Global CSS
Imported once, typically in the root layout, and applies everywhere:
/* app/globals.css */
:root { --color-primary: #0070f3; }
body { margin: 0; font-family: system-ui, sans-serif; }
// app/layout.tsx
import './globals.css';
CSS Modules — scoped by default
A file named *.module.css is automatically scoped to the component that imports it, so class names never collide across the app:
/* Card.module.css */
.card {
border: 1px solid #eaeaea;
border-radius: 8px;
padding: 1rem;
}
import styles from './Card.module.css';
export default function Card({ children }: { children: React.ReactNode }) {
return <div className={styles.card}>{children}</div>;
}
Tailwind CSS — the most common choice today
create-next-app offers Tailwind as a setup option; utility classes are written directly in JSX and compiled/purged at build time:
export default function Card({ children }: { children: React.ReactNode }) {
return (
<div className="rounded-lg border border-gray-200 p-4 shadow-sm">
{children}
</div>
);
}
Tailwind pairs particularly well with the App Router because utility classes work identically whether a component is a Server or Client Component — no runtime JS is needed to apply styles.
A note on CSS-in-JS libraries
Older CSS-in-JS libraries (styled-components, Emotion) that generate styles at runtime need special handling to work with Server Components, since they typically rely on React context and client-side style injection. Some libraries now ship App Router-compatible setups, but many teams migrate to Tailwind or CSS Modules specifically to avoid this friction — check a library's current App Router support before adopting it for a new project.
next/font — self-hosted, zero-layout-shift fonts
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'] });
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={inter.className}>
<body>{children}</body>
</html>
);
}
next/font downloads and self-hosts Google Fonts at build time (no request to Google's servers at runtime) and automatically avoids layout shift by injecting the right size-adjust metrics.