跳到主要内容

CSS

Next.js 提供了几种使用 CSS 来样式化应用程序的方式,包括:

CSS Modules

CSS Modules 通过生成唯一的类名来本地化 CSS 作用域。这允许你在不同文件中使用相同的类,而不必担心命名冲突。

要开始使用 CSS Modules,创建一个扩展名为 .module.css 的新文件,并将其导入到 app 目录内的任何组件中:

app/blog/blog.module.css
.blog {
padding: 24px;
}

app/blog/page.tsx
import styles from './blog.module.css'

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

要开始使用 CSS Modules,创建一个扩展名为 .module.css 的新文件,并将其导入到 pages 目录内的任何组件中:

/styles/blog.module.css
.blog {
padding: 24px;
}
pages/blog/index.tsx
import styles from './blog.module.css'

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

全局 CSS

你可以使用全局 CSS 在整个应用程序中应用样式。

创建一个 app/global.css 文件并在根布局中导入它,以将样式应用到应用程序中的每个路由

app/global.css
body {
padding: 20px 20px 60px;
max-width: 680px;
margin: 0 auto;
}
app/layout.tsx
// 这些样式适用于应用程序中的每个路由
import './global.css'

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

注意: 全局样式可以导入到 app 目录内的任何布局、页面或组件中。但是,由于 Next.js 使用 React 内置的样式表支持来与 Suspense 集成,目前在路由之间导航时不会移除样式表,这可能导致冲突。我们建议将全局样式用于真正的全局 CSS,并将 CSS Modules 用于作用域 CSS。

pages/_app.js 文件中导入样式表,以将样式应用到应用程序中的每个路由

pages/_app.js
import '@/styles/global.css'

export default function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}

由于样式表的全局性质,为了避免冲突,你应该在 pages/_app.js 内导入它们。

外部样式表

外部包发布的样式表可以导入到 app 目录中的任何地方,包括并置组件:

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

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

注意: 在 React 19 中,也可以使用 <link rel="stylesheet" href="..." />。请参阅 React link 文档了解更多信息。

Next.js 允许你从 JavaScript 文件导入 CSS 文件。这是可能的,因为 Next.js 扩展了 import 的概念,超越了 JavaScript。

node_modules 导入样式

自 Next.js 9.5.4 以来,允许从 node_modules 导入 CSS 文件到应用程序的任何地方。

对于全局样式表,如 bootstrapnprogress,你应该在 pages/_app.js 内导入文件。例如:

pages/_app.js
import 'bootstrap/dist/css/bootstrap.css'

export default function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}

要导入第三方组件所需的 CSS,你可以在组件中这样做。例如:

components/example-dialog.js
import { useState } from 'react'
import { Dialog } from '@reach/dialog'
import VisuallyHidden from '@reach/visually-hidden'
import '@reach/dialog/styles.css'

function ExampleDialog(props) {
const [showDialog, setShowDialog] = useState(false)
const open = () => setShowDialog(true)
const close = () => setShowDialog(false)

return (
<div>
<button onClick={open}>Open Dialog</button>
<Dialog isOpen={showDialog} onDismiss={close}>
<button className="close-button" onClick={close}>
<VisuallyHidden>Close</VisuallyHidden>
<span aria-hidden>×</span>
</button>
<p>Hello there. I am a dialog</p>
</Dialog>
</div>
)
}

排序和合并

Next.js 在生产构建期间通过自动分块(合并)样式表来优化 CSS。你的 CSS 顺序取决于你在代码中导入样式的顺序

例如,base-button.module.css 将在 page.module.css 之前排序,因为 <BaseButton>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} />
}
base-button.tsx
import styles from './base-button.module.css'

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

建议

为了保持 CSS 排序的可预测性:

  • 尝试将 CSS 导入包含在单个 JavaScript 或 TypeScript 入口文件中
  • 在应用程序的根目录中导入全局样式和 Tailwind 样式表。
  • 对嵌套组件使用 CSS Modules 而不是全局样式。
  • 为你的 CSS 模块使用一致的命名约定。例如,使用 <name>.module.css 而不是 <name>.tsx
  • 将共享样式提取到共享组件中以避免重复导入。
  • 关闭自动排序导入的 linter 或格式化工具,如 ESLint 的 sort-imports
  • 你可以在 next.config.js 中使用 cssChunking 选项来控制 CSS 的分块方式。

开发与生产

  • 在开发中(next dev),CSS 更新通过快速刷新立即应用。
  • 在生产中(next build),所有 CSS 文件都会自动连接成多个压缩和代码分割.css 文件,确保为路由加载最少的 CSS。
  • CSS 在生产中仍然在禁用 JavaScript 的情况下加载,但在开发中需要 JavaScript 进行快速刷新。
  • CSS 排序在开发中的行为可能不同,始终确保检查构建(next build)以验证最终的 CSS 顺序。