在 Next.js 14 中引入组件级 CSS 时,必须使用 .module.css 后缀,并通过 import styles from './X.module.css' 的方式导入,再使用 className={styles.xxx} 绑定样式;普通 .css 文件不具备组件作用域,若使用动态导入或错误路径,还可能引发 FOUC(样式闪烁)或模块解析失败。

组件级 CSS 必须用 .module.css 后缀
在 Next.js 中,普通的 .css 文件不能直接用于组件级作用域样式:要么会直接报错,要么样式会失去隔离并污染全局。若想真正实现组件内部样式隔离,标准做法就是使用 CSS Modules。而 Next.js 是否将其识别为模块化样式,关键判断依据只有一个——文件名必须以 .module.css 结尾。注意不要写成 .css.module,也不能误写为 .modules.css。
常见错误场景:import './Button.css' 表面上似乎可以运行,但类名不会被哈希处理,样式也会泄漏到其他组件;如果构建时启用了更严格的校验,还可能触发警告或构建提示。
Button.module.css✅ 命名正确,Next.js 会自动识别并启用 CSS ModulesButton.css❌ 属于普通 CSS,仅适合全局样式场景(并且通常只能在_app.tsx或app/layout.tsx中引入)Button.modules.css❌ 文件名拼写错误,Next.js 不会将其识别为模块样式
导入后必须通过对象解构使用类名
这里不能再像全局 CSS 那样直接写 className="btn"。原因很明确:CSS Modules 导出的并不是普通字符串类名,而是一个映射对象,最终生效的类名会被编译成带哈希的唯一标识。因此,正确写法应该是先 import styles from './Button.module.css',再通过 className={styles.btn} 来引用对应样式。
常见易错点:
- 写成
className="btn"→ 样式完全不生效,控制台通常也不会报错,排查成本很高 - 写成
className={styles['btn']}→ 虽然可以运行,但会影响类型推导和 IDE 自动提示,通常没有必要 - 在
useClient组件中漏写"use client"声明 → 如果组件包含状态或事件逻辑,服务端渲染阶段可能直接失败
App Router 下路径和导入位置无限制,但不能动态导入
在 app/ 目录结构下,你可以将 .module.css 文件放在任意层级,例如 app/components/Button.module.css,并在对应组件中直接通过 import 引入,Next.js 会正确处理服务端渲染(SSR)以及客户端 hydration。
但有几点必须特别注意:
import('./Button.module.css')或require('./Button.module.css')❌ 动态导入无法参与服务端样式提取,容易造成 FOUC(首屏样式闪烁)或样式丢失- 在
app/layout.tsx中 import 组件级 CSS ❌ 实际意义不大——它会被当作全局依赖注入到所有路由中,失去组件级 CSS 的隔离价值 - 导入路径通常应保持相对路径,不能直接使用绝对路径别名(如
@/styles/Button.module.css),除非你已经在tsconfig.json中正确配置了paths,否则构建阶段很容易出现模块解析失败
与 Tailwind 混用时不要套娃写 class
很多开发者希望“同时使用 Tailwind 和 CSS Modules”,于是写出类似 className={`${styles.container} text-lg bg-blue-500`} 的代码。表面上看这样能工作,但实际上可能破坏 Tailwind 的 PurgeCSS 安全性——某些未被静态识别的 class,存在被错误清理的风险。
更推荐的做法是:
- 纯原子类样式逻辑 → 直接全部使用 Tailwind,无需额外引入
.module.css - 需要复用的复杂样式块 → 放入
.module.css中,再通过styles.xxx引用,Tailwind 类只用于局部微调(例如className={`${styles.card} p-4`}中的p-4是安全的,因为它以显式字符串形式出现) - 尽量避免在
.module.css中编写@layer utilities,或通过嵌套@apply去引用 Tailwind 类——如果 PostCSS 插件链配置不一致,构建过程可能静默失败
最容易被忽视的一点是:组件级 CSS 在本地开发环境下看起来可能一切正常,但到了生产构建阶段,如果文件名没有使用 .module.css 后缀,或者仍然使用 className="xxx" 这种字符串硬编码方式,样式就可能彻底失效——通常没有明确报错,只会表现为页面空白、布局错位或样式消失。
