Can you predict how Next.js handles CSS import order? This interactive quiz reveals a hidden behavior that might surprise you.
π For the Impatient
Want to see the magic immediately? Here are all the links:
π Repository: https://github.com/ale-grosselle/css-order-next.js
π§ͺ Live Test Cases:
- Case A - Basic CSS ordering
- Case B - Server component CSS
- Case C - Client component follows rules
- Case D - The mind-bender! π€―
π΅οΈββοΈ Pro Tip: Open your browser's DevTools (F12 or right-click β Inspect) and check the Elements panel to see the actual CSS order loaded for each case. This is crucial for understanding how Next.js handles CSS imports!
We all know the golden rule from the Next.js documentation: "CSS import order matters". It's a fundamental principle we've relied on for years. But how well do you really understand CSS ordering in Next.js?
Let's put your knowledge to the test with a hands-on quiz that reveals some surprising behavior you probably didn't know about.
The Setup: Our CSS Ordering Laboratory
I've created a simple Next.js project to test CSS import behavior. Here's what we're working with:
Base Configuration
Layout Component (layout.tsx
)
import "./globals.css";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return <div>{children}</div>;
}
Global Styles (globals.css
)
div.foo {
--layout-file: red;
background-color: var(--layout-file);
}
This sets all .foo
divs to red by default.
Our Test Stylesheets
We have several CSS files that all target the same element with different colors:
1.global.css
- Makes divs yellow:
div.foo {
--1-global-page-css: yellow;
background-color: var(--1-global-page-css);
}
0.module.css
- CSS Module for brown divs:
div.body-module {
--0--page--css--module: brown;
background-color: var(--0--page--css--module);
}
Server Component CSS (components/2.css
):
div.foo {
--2-rsc-component: orange;
background-color: var(--2-rsc-component);
}
Client Component CSS (components/3.css
):
div.foo {
--3-client-component: black;
background-color: var(--3-client-component);
}
Now, let's see how well you can predict CSS ordering behavior!
π§© Quiz Time: Can You Guess the Color?
Case A: The Baseline Test
π Try Case A Live
// case-a/page.tsx
import "../1.global.css";
import style from "../0.module.css";
export default function HomePage() {
return (
<div className={`${style["body-module"]} foo`}>HELLO</div>
);
}
π€ Your Prediction: What background color will the div have?
A) Red (from globals.css
)
B) Yellow (from 1.global.css
)
C) Brown (from 0.module.css
)
β Correct Answer: C) Brown
Why: Layout CSS (globals.css
) is loaded first in a separate bundle. Within the page bundle, 0.module.css
is imported LAST, so at equal specificity it wins over both globals.css
and 1.global.css
.
Case B: Adding a Server Component
π Try Case B Live
// case-b/page.tsx
import "../1.global.css";
import style from "../0.module.css";
import { Example } from "@/components/Example"; // Server component with CSS
export default function HomePage() {
return (
<>
<div className={`${style["body-module"]} foo`}>HELLO</div>
<Example />
</>
);
}
The Example
component imports ./2.css
which makes .foo
divs orange.
π€ Your Prediction: What color will the div be now?
A) Red (from globals.css
)
B) Brown (from 0.module.css
)
C) Orange (from server component 2.css
)
β Correct Answer: C) Orange
Why: Server component CSS follows import order. The Example
component is imported LAST, so its CSS comes after everything else and wins.
Case C: Still Following the Rules
π Try Case C Live
// case-c/page.tsx
import { Example } from "@/components/Example";
import "../1.global.css";
import style from "../0.module.css";
import { ExampleClientComponent } from "@/components/Example-client-component"; // Client component!
export default function HomePage() {
return (
<>
<div className={`${style["body-module"]} foo`}>HELLO</div>
<ExampleClientComponent />
<Example />
</>
);
}
Now we have:
-
Example
(server component with orange CSS) imported FIRST -
1.global.css
(yellow) imported SECOND -
0.module.css
(brown) imported THIRD -
ExampleClientComponent
(client component with black CSS) imported LAST
π€ Your Prediction: Based on import order, what color should win?
A) Orange (server component imported first)
B) Brown (from 0.module.css
imported third)
C) Black (client component imported last)
β Expected Answer: C) Black
Why: Following the "last import wins" rule, since ExampleClientComponent
is imported last, its CSS should come last and win.
π Expected CSS Loading Order:
-
Layout Bundle:
globals.css
(red) -
Page Bundle:
components/2.css
(orange) β1.global.css
(yellow) β0.module.css
(brown) βcomponents/3.css
(black) β Should win
And indeed, the div IS black! The rule still holds...
Case D: The Mind-Bender That Breaks Everything
π Try Case D Live
Let's try one more test to confirm our suspicions:
// case-d/page.tsx
import { ExampleClientComponent } from "@/components/Example-client-component"; // FIRST
import { Example } from "@/components/Example";
import "../1.global.css"; // THIRD
import style from "../0.module.css"; // LAST CSS import
export default function HomePage() {
return (
<>
<div className={`${style["body-module"]} foo`}>HELLO</div>
<ExampleClientComponent />
<Example />
</>
);
}
Now the client component is imported FIRST, and 0.module.css
is imported LAST.
π€ Final Prediction: Based on the "last import wins" rule, what color should the div be?
A) Black (client component imported first)
B) Brown (from 0.module.css
imported last)
C) Orange (from server component)
π€― Answer: Still Black!
The Hidden Truth Revealed: Even though 0.module.css
is imported LAST and should win according to the fundamental CSS ordering rule, the client component's CSS still wins!
π Expected CSS Loading Order:
-
Layout Bundle:
globals.css
(red) -
Page Bundle:
components/3.css
(black - imported first) βcomponents/2.css
(orange) β1.global.css
(yellow) β0.module.css
(brown) β Should win!
π Actual CSS Loading Order:
-
Layout Bundle:
globals.css
(red) -
Page Bundle:
components/2.css
(orange) β1.global.css
(yellow) β0.module.css
(brown) -
Client Bundle:
components/3.css
(black) β Always wins!
This completely breaks the "last import wins" rule! Client component CSS gets moved to the end regardless of where you import it.
π The Discovery: Next.js Has a unclear CSS Rule
What I learned:
- β Layout CSS is always loaded first (separate bundle)
- β Server component CSS follows import order within the page
- β Client component CSS always loads after server component CSS, regardless of import order.
- β Among client components, import order between multiple client components is still respected
π¨ MUST-Follow Rules to Avoid CSS Chaos
After discovering this hidden behavior, the Next.js CSS recommendations become absolute requirements, not just suggestions:
1. Use CSS Modules for ALL Component-Specific Styles
// β
DO: Always use CSS Modules for components
import styles from "./MyComponent.module.css";
// β DON'T: Never use global CSS in components
import "./MyComponent.css"; // This creates ordering conflicts!
Why this is critical: CSS Modules provide scoping that prevents the CSS ordering conflicts we discovered. Your styles won't accidentally override or be overridden by other components.
2. Import CSS Only Once Per Component
// β
DO: One CSS import per component
import styles from "./Button.module.css";
export const Button = () => <button className={styles.button}>Click me</button>;
// β DON'T: Multiple CSS imports in one component
import "./theme.css";
import "./button.css";
import "./overrides.css"; // Unpredictable ordering!
Why this matters: Multiple CSS imports create dependency chains that behave differently for server vs client components.
3. Define Global CSS ONLY in Layout
// β
DO: Only in layout.tsx
import "./globals.css";
import "./tailwind.css"; // If using Tailwind
// β DON'T: Global CSS anywhere else
// pages/home.tsx - NEVER do this!
import "../globals.css"; // This breaks predictability
Why this is essential: Global CSS should be loaded once and consistently. Importing it multiple places creates the exact ordering conflicts we discovered.
Did this quiz surprise you? Have you encountered similar CSS ordering mysteries in your Next.js projects? Share your experiences in the comments!
Top comments (0)