Fix Next.js Global CSS cannot be imported: Pages Router
A working global Sass entry point, the rename-only trap, and the different rule for App Router projects, checked against current Next.js builds.
TL;DR#
In a Pages Router project, move first-party global CSS or Sass imports from ordinary components into pages/_app.js. Keep the stylesheet global, and forward pageProps to the active page. For component-specific styles, use an actual CSS Module and its exported class names. App Router has different import rules: its application-wide stylesheet normally belongs in the root layout.
The error in a current Pages Router build#
The Stack Overflow question shows a shared Layout component importing main.scss, which ultimately contains a body reset. An image-plugin installation preceded the failure, but the posted configuration also used the legacy Sass wrapper. The report does not establish that installing the image package itself caused the CSS restriction.
The restriction still affects a minimal Pages-only project. On 13 October 2026, builds with Next.js 16.4.0, React 19.3.0, Sass 1.105.1 and Node.js 22.23.3 reproduced this error with both Webpack and the default Turbopack compiler:
Global CSS cannot be imported from files other than your Custom <App>.The import originated in components/Layout.js and pointed to ../styles/main.scss. Follow the component location and import path in your own diagnostic: the filename of the stylesheet identifies the rejected resource, while the importing JavaScript file identifies the place to change. Renaming the component to App does not turn it into the framework's Custom App entry point.
Why it happens#
A plain .scss stylesheet can emit selectors that affect the entire document. A body rule does not become local merely because its import sits next to a component. The official error reference explains the Pages Router restriction in terms of global effects and stylesheet ordering. Centralising application-wide imports in the Custom App gives them a deliberate entry point.
There are two useful decisions here: whether the declarations are global, and which router owns the route. The build failure demonstrated below is about first-party global Sass in a Pages-only project. It is not evidence that every stylesheet from every dependency is forbidden in components. The Pages CSS documentation explicitly permits package CSS imports in other locations, while recommending _app for application-wide package styles.
Fix#
1. Use the built-in Sass integration#
For an .scss entry point, install the Sass compiler in the project where Next.js builds:
npm install --save-dev sassThe Sass guide documents built-in .scss and .sass support after installation. A .css stylesheet does not require Sass. Make sure the environment that performs the build installs the development dependencies when using the command above.
If your configuration still wraps its export with @zeit/next-sass, remove that legacy Sass wrapper and its loader-specific cssModules setting. Preserve unrelated configuration rather than deleting the whole file. The accepted answer's replacement of the old loader is relevant to that posted setup; it is not a requirement to erase image settings, redirects or other working options in every project.
For the current reproduction, the built-in integration was already in place before the failing build. This matters: changing the Sass integration and moving the import simultaneously would not show which change resolved the current error.
2. Move the global import into the Custom App#
Create or edit the existing pages/_app.js entry point. With a sibling styles directory, its minimal content is:
// pages/_app.js
import '../styles/main.scss';
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />;
}Remove the same global import from components/Layout.js. Keep the layout's rendering and any unrelated imports:
// components/Layout.js
export default function Layout({ children }) {
return <div className="frame">{children}</div>;
}The stylesheet remains an ordinary global stylesheet. For example:
/* styles/main.scss */
body {
margin: 0;
}
.frame {
width: 90%;
margin: auto;
}The import path is relative to _app.js; adjust it to your actual directory layout. If _app already contains providers or a shared layout, add the import there and retain those wrappers. The single change in the successful reproduction was the import location: the Sass contents, layout markup, page and dependency versions stayed the same.
3. Keep the page data under its real prop name#
The question's attempted fix destructured props instead of pageProps. That is a separate rendering bug. Next.js passes initial page data through pageProps, as the Custom App documentation describes.
To exercise that path, a Pages Router page can return a visible marker:
// pages/index.js
import Layout from '../components/Layout';
export default function Page({ message }) {
return <Layout><p>{message}</p></Layout>;
}
export function getStaticProps() {
return { props: { message: 'Page prop received' } };
}With the correct _app above, the generated paragraph contains the marker. Changing only _app to read and spread props still lets the CSS build succeed, but produces an empty paragraph. That failure does not mean the global stylesheet import stopped working. If a later build instead reports that a Date cannot be serialised in page props, continue with the Pages Router Date serialisation fix: it addresses the values returned by the data-fetching function.
Verify the fix#
Run the same production build command that failed originally. In the reproduction, both the explicit Webpack build and the default compiler accepted the relocated import:
npx next build --webpack
npx next buildYou only need the compiler used by your project to verify your repair. Running both here established that switching compilers was not the solution. The repaired Webpack build emitted a stylesheet containing the original body and .frame declarations and generated the expected page paragraph. The default Turbopack build also returned success and generated that paragraph.
Inspect visible markup separately from embedded page data. Searching the entire HTML file for the marker can give a misleading result: in the wrong-prop control, the marker was still present in the serialised page data, although the actual paragraph was empty. For a browser check, inspect the displayed paragraph and the computed styles on body and the layout element. These production build checks establish emitted CSS and HTML; they do not constitute a browser rendering test.
If you created _app.js for the first time while the development server was already running, restart that server. The Custom App documentation specifically requires this for a newly added entry point. It is different from repeatedly restarting an unchanged failing import and expecting it to become valid.
One email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
Why a filename-only Sass Module conversion fails#
One answer suggests adding .module to the filename. That is appropriate only if the stylesheet actually contains component-scoped styles. Renaming the reproduction's global file to main.module.scss while keeping its contents produced this Webpack diagnostic:
Syntax error: Selector "body" is not pure (pure selectors must contain at least one local class or
id)The bare body selector remained global. A useful conversion separates the reset from the layout class: keep body in the global entry point, and put the local class into a module:
/* styles/Layout.module.scss */
.frame {
width: 90%;
margin: auto;
}Then import the module's class mapping and use that mapping in the component:
// components/Layout.js
import styles from '../styles/Layout.module.scss';
export default function Layout({ children }) {
return <div className={styles.frame}>{children}</div>;
}The local-class module passed the tested Webpack build. CSS Modules generate local class names, so retaining only a literal className="frame" would not select the module's generated class. Decide which declarations belong to the document and which belong to the component before choosing the filename; the extension is part of a real conversion, not an escape from global style semantics.
App Router uses the root layout, not Custom App#
For routes under app, use the App Router CSS guidance. It permits global styles in App Router layouts, pages and components. Import an application-wide reset in the root layout to make its intended scope clear; adding pages/_app.js does not supply the App Router entry point.
A separate App Router control with the same dependency versions imported the same global Sass from a child component. Its default Turbopack production build succeeded and generated the child component's text. This directly checked the router boundary rather than assuming that a Pages Router failure applies everywhere.
The documentation also explains that globally imported styles are not currently removed on navigation between routes. A component import therefore does not make those selectors component-local. Use CSS Modules when that isolation is the goal. This distinction avoids applying the old Pages Router answer to a different routing system merely because both projects use Next.js and Sass.
Finally, preserve the image behaviour that prompted the original configuration change. If an SVG import now returns an object where you expected a component, the Next.js SVG import guide explains static image imports and the separate SVGR setup for each compiler. If the stylesheet builds but an image under public/static returns a 404, continue with Next.js public image paths: the browser URL starts at the public root, rather than including /public.
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
Continue Reading
Fix: Next.js proxy.js matcher not working for static assets
Without a matcher, proxy.js runs on every request including _next/static and public assets. And Server Functions are not separate routes — a matcher gap silently drops auth coverage.
Fix Error: NEXT_REDIRECT logged in a Route Handler catch
Reproduced on Next.js 16.4.2: why redirect() inside try/catch logs Error: NEXT_REDIRECT, sends users to the wrong URL or returns a 500, and the three fixes that hold.
Next.js Redirect from / to another page
If your redirect works only after render, flickers, or fires in the wrong place, you are probably using the wrong redirect API for the job. Here is the exact mapping.


