首页 / Next.js 16 入门教程 / 样式方案(上)

Next.js 16 入门教程

样式方案(上)

本教程共 42 篇 · 第 15 篇 · 更新于 2026-07-30 · 约 7 分钟阅读

Next.jsNext.js 16 入门教程CSSCSS ModulesCSS-in-JS

15. 样式方案(上)

本节目标:了解 Next.js 支持的所有样式方案,理解 CSS Modules、全局 CSS 和 CSS-in-JS 的工作原理,学会在不同场景下选择合适的样式方案。

为什么样式方案这么多?

前端样式一直是令人头疼的问题。组件多了,类名容易冲突;样式大了,加载速度变慢。Next.js 作为全栈框架,提供了多种样式方案供你选择。

没有哪一种方案是绝对最好的。理解每种方案的特点,才能在实际项目中做出合理选择。

CSS Modules

CSS Modules 是 Next.js 内置支持的特性,它的核心思路是:局部作用域

你可以用普通 CSS 写样式,但类名会被自动转换成唯一的标识符。这样,你在不同文件里写同名类名也不会冲突。

怎么用?

创建一个 .module.css 文件,然后在组件中引入:

/* app/blog/blog.module.css */
.blog {
  padding: 24px;
  max-width: 800px;
  margin: 0 auto;
}

.title {
  font-size: 2rem;
  font-weight: bold;
}
// app/blog/page.tsx
import styles from './blog.module.css'

export default function Page() {
  return (
    <main className={styles.blog}>
      <h1 className={styles.title}>我的博客</h1>
    </main>
  )
}
Note

CSS Modules 的类名在构建时会被自动转换为类似 blog_blog_x7k9a 的唯一标识符,确保不会与其他文件的类名冲突。

适合什么场景?

CSS Modules 适合中大型项目。每个组件的样式独立管理,不用担心全局污染。团队成员并行开发时,也不会因为类名重复导致样式被覆盖。

全局 CSS

全局 CSS 是最传统的方式。你写一个 CSS 文件,在根布局中引入,样式就会应用到所有页面。

/* app/global.css */
body {
  padding: 20px 20px 60px;
  max-width: 680px;
  margin: 0 auto;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}

a {
  color: #0070f3;
  text-decoration: none;
}
// app/layout.tsx
import './global.css'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="zh">
      <body>{children}</body>
    </html>
  )
}

需要注意的地方

全局 CSS 可以引入到任意布局或组件中。但官方有个提醒:由于 Next.js 对样式表的处理方式,路由切换时样式表不会被移除,可能导致冲突。

Tip

推荐做法:全局 CSS 只用于真正全局的样式(比如 CSS Reset、基础排版),组件级别的样式用 CSS Modules 或 Tailwind CSS。

外部样式表

你想用 Bootstrap、Ant Design 这些第三方库的 CSS?直接在组件里引入就行:

// app/layout.tsx
import 'bootstrap/dist/css/bootstrap.css'

export default function RootLayout({ children }) {
  return (
    <html lang="zh">
      <body className="container">{children}</body>
    </html>
  )
}

从 React 19 开始,还可以用 <link> 标签引入样式表,这也是官方推荐的另一种方式。

CSS-in-JS

CSS-in-JS 是把 CSS 写在 JavaScript 里的方案。它让你可以动态生成样式,还能利用 JavaScript 变量。

Next.js 的 App Router 支持多种 CSS-in-JS 库,比如 styled-componentsstyled-jsxvanilla-extractstylex 等。

配置三步走

在 App Router 中使用 CSS-in-JS 需要三步:

  1. 创建一个样式注册表(Style Registry)
  2. useServerInsertedHTML 钩子注入样式
  3. 用注册表组件包裹你的应用

styled-components 示例

先在 next.config.ts 中启用:

// next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  compiler: {
    styledComponents: true,
  },
}

export default nextConfig

然后创建注册表组件:

// lib/registry.tsx
'use client'

import React, { useState } from 'react'
import { useServerInsertedHTML } from 'next/navigation'
import { ServerStyleSheet, StyleSheetManager } from 'styled-components'

export default function StyledComponentsRegistry({
  children,
}: {
  children: React.ReactNode
}) {
  const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet())

  useServerInsertedHTML(() => {
    const styles = styledComponentsStyleSheet.getStyleElement()
    styledComponentsStyleSheet.instance.clearTag()
    return <>{styles}</>
  })

  if (typeof window !== 'undefined') return <>{children}</>

  return (
    <StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
      {children}
    </StyleSheetManager>
  )
}

最后在根布局中使用:

// app/layout.tsx
import StyledComponentsRegistry from './lib/registry'

export default function RootLayout({ children }) {
  return (
    <html lang="zh">
      <body>
        <StyledComponentsRegistry>{children}</StyledComponentsRegistry>
      </body>
    </html>
  )
}

styled-jsx 示例

styled-jsx 是 Next.js 官方提供的 CSS-in-JS 方案,不需要额外配置:

export default function Card() {
  return (
    <div className="card">
      <h2>标题</h2>
      <p>内容</p>
      <style jsx>{`
        .card {
          padding: 16px;
          border-radius: 8px;
          box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
        }
        h2 {
          margin: 0 0 8px;
        }
      `}</style>
    </div>
  )
}
Warning

CSS-in-JS 在 Server Components 和流式渲染中的支持要求库作者兼容 React 18+ 的新特性。使用前确认你的库版本是否支持。

样式合并与优先级

Next.js 在生产构建时会自动合并和压缩 CSS 文件。样式的最终顺序取决于你在代码中的引入顺序。

比如 BaseButton 组件先引入 base-button.module.css,然后页面引入 page.module.css,那么后者的样式优先级更高。

// page.tsx
import { BaseButton } from './base-button'
import styles from './page.module.css'

export default function Page() {
  return <BaseButton className={styles.primary} />
}

推荐做法

想保持样式顺序可预测,记住几点:

  • 把 CSS 限制在单一入口文件中引入
  • 全局样式和 Tailwind 在根布局引入
  • 大部分样式需求用 Tailwind CSS 的工具类
  • Tailwind 不够用时,用 CSS Modules 补充组件样式
  • 关闭 ESLint 的 sort-imports 规则,避免自动排序打乱样式顺序

开发 vs 生产环境

在开发环境(next dev),CSS 修改会即时生效,配合 Fast Refresh 体验很好。

在生产环境(next build),所有 CSS 文件会被自动合并压缩成多个小型 .css 文件,确保每个路由只加载必要的样式。

Note

开发环境中的 CSS 排序可能和生产环境不一致。上线前记得跑一次 next build 验证最终效果。

方案对比总结

方案优点缺点适用场景
CSS Modules局部作用域、零运行时开销类名不可读中大型项目组件样式
全局 CSS简单直接容易命名冲突全局基础样式
外部样式表直接用第三方库可能有冗余Bootstrap/Ant Design 等
CSS-in-JS动态样式、组件封装运行时开销、配置复杂需要动态主题的场景

下一节我们会深入 Tailwind CSS,这是目前 Next.js 官方推荐的样式方案。