全局CSS只能在app/layout.tsx顶层静态导入;App Router下_pages/_app.js已弃用,import必须为首行语句且不可加'use client',否则SSR样式注入失败。

全局CSS只能在 app/layout.tsx 顶层静态导入
Next.js App Router 下,_app.js 已被完全弃用,任何在 pages/_app.js 里写的全局 CSS 导入都会被忽略,甚至触发构建警告。唯一合法且生效的位置是 app/layout.tsx 的最顶部(即文件第一行 import),且不能加 'use client' —— 因为样式注入是服务端行为,加了反而会阻断 SSR 阶段的样式收集。
常见错误现象:
-
Global CSS cannot be imported from files other than your Custom _app.js:说明你误用了 Pages Router 的规则,或路径写错 - 样式在开发时可见、构建后丢失:大概率是
import写在了export default function RootLayout函数体内,或前面有其他语句(包括注释) - Tailwind 指令不生效:比如
@tailwind base没出现在导入的 CSS 文件中,或该文件被 postcss.config.js 错误地处理为模块
正确姿势:
- 把全局样式统一收口到
app/globals.css(官方推荐路径) - 在
app/layout.tsx第一行写import '@/app/globals.css' - 确保该 CSS 文件只含标准 CSS 规则或
@tailwind指令,不含:global或module相关语法
globals.css 里该放什么、不该放什么
这个文件本质是“全局作用域入口”,不是设计系统抽象层。它适合放:
立即学习“前端免费学习笔记(深入)”;
- CSS reset 或 normalize 规则(如
* { margin: 0; padding: 0; }) - HTML 元素默认样式(
a { color: inherit; text-decoration: none; }) - Tailwind 的三段式指令:
@tailwind base;、@tailwind components;、@tailwind utilities; - 少量真正跨全站的类名,比如
.sr-only、.visually-hidden
不适合放:
- 组件级样式(如
.button {})—— 应该走 CSS Modules 或styled方案 - 主题变量或 CSS 自定义属性定义(
:root { --color-primary: #0070f3; })—— 这些更适合放进stitches.config.ts或theme.ts等 JS 配置中,便于类型安全和 SSR 提取 - 媒体查询封装(如
@media (min-width: 768px) { ... })—— 容易失控,响应式逻辑应交给组件自身或工具函数(如 Stitches 的bp)
为什么不能在组件里 import 全局 CSS
Next.js 在构建期会对 CSS 依赖做静态分析,只有 app/layout.tsx 的顶层 import 才会被识别为“需要提取并注入到 的全局样式”。其他位置的导入,比如:
- 在
app/page.tsx里写import '../styles/global.css'→ 构建报错 - 在 Client Component 中动态
import('./Button.css')→ 报CSS cannot be imported dynamically - 在
useEffect里通过insertRule注入样式 → SSR 阶段无样式,首屏闪动
根本原因不是“语法不允许”,而是 Next.js 的服务端样式抽取(CSS SSR)机制只扫描根布局的顶层依赖。绕过它等于放弃 SSR 样式支持,对 SEO 和首屏性能是硬伤。
混用 App Router 和 Pages Router 时的样式隔离
如果项目同时存在 app/ 和 pages/ 目录(过渡期常见),两套路由的全局样式完全不共享:
-
app/layout.tsx中的import只影响所有app/下的路由 -
pages/_app.js中的import只影响所有pages/下的页面 - 两者不能互相调用、也不能共用同一份 CSS 文件路径(除非路径明确指向同一物理文件,但语义上仍是两个独立注入点)
最容易被忽略的一点:当你删掉 pages/ 但没清理 pages/_app.js,Next.js 仍可能读取它并报错——务必确认项目结构干净,只保留一套路由体系。


















