CSS Modules 样式名找不到的主因是 styles 对象未导出对应键,根源在于文件未被识别为模块(后缀非 .module.css、导入方式错误)或 JS 中访问名与编译后键名不匹配(如连字符未转驼峰或未配 localsConvention)。

样式名找不到,基本不是写错了类名,而是 styles 对象压根没导出那个键——要么文件没被识别为模块,要么类名在 JS 中访问方式不匹配编译后的实际键名。
文件名和导入方式不满足 CSS Modules 硬性要求
CSS Modules 不是全局开关,它只对特定后缀的文件生效,且必须用特定方式导入。
- 文件名必须带
.module.css(或.module.scss等,且构建工具已配对应 loader);Button.css或button.module.css都不会触发模块化 - 导入必须是
import styles from './Button.module.css';require('./Button.module.css')返回的是字符串路径,styles是空对象 - Webpack 用户要检查
css-loader的modules配置是否只匹配/\.module\.css$/,避免把normalize.css也哈希化 - Vite 用户确认没在
vite.config.ts中设css.modules: false
类名在 JS 中访问方式与编译结果不一致
CSS 文件里写的 .pl-6、.toolbar-container,JS 中不能直接写 styles.pl-6(语法错误)或 styles["pl-6"](可能返回 undefined)。
- 默认行为是连字符转驼峰:
.pl-6→styles.pl6,.toolbar-container→styles.toolbarContainer - 想保留原始连字符形式,Vite 需配
css.modules.localsConvention: 'dashes',Webpack 需在css-loader中设localsConvention: 'dashes' - TypeScript 报
TS2339通常是因为类型声明没同步更新;推荐用typescript-plugin-css-modules自动生成.d.ts,并开启"namedExports": true - 在组件里加
console.log(styles),一眼就能看出实际导出的键名长什么样
动态拼接时混用 styles 和字面量字符串
className={`${styles.btn} ${isPrimary ? 'primary' : ''}` 这种写法会让 'primary' 完全脱离 CSS Modules 处理链,构建工具既不打包它,运行时也找不到对应样式。
立即学习“前端免费学习笔记(深入)”;
- 所有类名都必须走
styles对象:正确写法是className={`${styles.btn} ${isPrimary ? styles.primary : ''}` - 多个条件建议用
clsx:className={clsx(styles.btn, isPrimary && styles.primary, isDisabled && styles.disabled)} - 如果要用 BEM 风格如
button--large,必须确保该类名已在 CSS 文件中静态声明(.button--large),再通过styles['button--large']访问 - 禁止在模板字符串里拼接
styles.xxx + ' ' + 'some-string',空格、null转字符串等都会导致静默失效
服务端渲染或 Server Component 中 styles 为空
Next.js App Router 的 Server Component 中,import styles from './X.module.css' 在服务端执行时返回空对象 {},因为 Node 环境无法解析 CSS 类名映射。
- 若组件需 SSR,必须标记
"use client",否则styles始终为空,客户端 hydrate 时 class 名不一致,引发 FOUC 或样式闪动 - TS 项目要配好
types/css-modules.d.ts,否则类型检查会放行,但运行时失败 - 不要依赖服务端生成的 class 字符串传给客户端;所有 className 必须在客户端用
styles动态计算
最常被忽略的是:你以为在写样式,其实是在调试构建链路。从文件后缀、导入语法、控制台打印 styles、到检查 TypeScript 类型声明,每一步都可能断开——而断点往往不在 CSS 本身。


















