Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NEXT-1308] Css is imported multiple times and out of order in /app dir #51030

Closed
1 task done
ssijak opened this issue Jun 9, 2023 · 124 comments
Closed
1 task done

[NEXT-1308] Css is imported multiple times and out of order in /app dir #51030

ssijak opened this issue Jun 9, 2023 · 124 comments
Assignees
Labels
area: app App directory (appDir: true) bug Issue was opened via the bug report template. linear: next Confirmed issue that is tracked by the Next.js team.

Comments

@ssijak
Copy link

ssijak commented Jun 9, 2023

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
      Platform: darwin
      Arch: arm64
      Version: Darwin Kernel Version 22.5.0: Mon Apr 24 20:52:24 PDT 2023; root:xnu-8796.121.2~5/RELEASE_ARM64_T6000
    Binaries:
      Node: 18.12.1
      npm: 8.19.2
      Yarn: 1.22.19
      pnpm: 7.18.1
    Relevant packages:
      next: 13.4.5-canary.9
      eslint-config-next: N/A
      react: 18.2.0
      react-dom: 18.2.0
      typescript: 5.1.3

Which area(s) of Next.js are affected? (leave empty if unsure)

App directory (appDir: true)

Link to the code that reproduces this issue or a replay of the bug

https://github.com/ssijak/next-css-issue-not-working-simple

To Reproduce

Just start the app and check the styling on the buttons. Styles are imported multiple times wherever Button was used (page and layout) and order is also not deterministic, so it can be imported in different order on different app runs.

This is another/same simple repro difference is just that it uses turbo and transpiles the UI lib, I started with that but figured that the issue is happening without it too https://github.com/ssijak/next-css-issue-not-working

Describe the Bug

-Same styles are imported multiple times
-Order of imports is not deterministic

Screenshot: https://share.cleanshot.com/nq35j7vh

Expected Behavior

Same styles should be imported once. Starting the app multiple times should not produce different results (ordering of CSS, impacting specificity)

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

From SyncLinear.com | NEXT-1308

@ssijak ssijak added the bug Issue was opened via the bug report template. label Jun 9, 2023
@github-actions github-actions bot added the area: app App directory (appDir: true) label Jun 9, 2023
@ssijak
Copy link
Author

ssijak commented Jun 9, 2023

This PR #50406 seems like it should have fixed this issue, but I see the problem in my repro repositories

@ssijak
Copy link
Author

ssijak commented Jun 9, 2023

CSS modules seem to be working ok. Removed vanilla-extract from this repro, and imported the same CSS from CSS module in the page and layout, and set it on the <button>, and it imports the CSS once.

Edit: it works like that if both the page and layout are RSC. When I use use client directive on the page it imports the same CSS twice https://share.cleanshot.com/HjhMJhCy

@igordanchenko
Copy link

This issue is still present in the latest canary (13.4.7-canary.1)

  1. Same styles are imported multiple times
  2. The order of imports is not deterministic

Here is a minimal repro with easy-to-reproduce test cases.

https://codesandbox.io/p/sandbox/next-51030-hlj722?file=%2Fapp%2Fpage.tsx%3A1%2C1

@tinleym
Copy link

tinleym commented Jun 21, 2023

The current order of imports wreaks havoc when CSS Cascade Layers are being used. The defined layer precedence that belongs in the root layout file gets read after the application of layers in child components. This causes the defined layer precedence to be ignored in favor of something random (https://css-tricks.com/css-cascade-layers/#aa-establishing-a-layer-order), and can make routes unusable.

This issue surfaced for me when updating up from 13.4.4 to 13.4.5 or 13.4.6.

@ddoan
Copy link

ddoan commented Jun 22, 2023

One workaround I'm currently using is to make the children CSS more specific: button.button instead of .button or div.something instead of .something.

@shuding shuding added the linear: next Confirmed issue that is tracked by the Next.js team. label Jun 23, 2023
@shuding shuding self-assigned this Jun 23, 2023
@shuding shuding changed the title Css is imported multiple times and out of order in /app dir [NEXT-1308] Css is imported multiple times and out of order in /app dir Jun 23, 2023
@shuding
Copy link
Member

shuding commented Jun 23, 2023

For https://codesandbox.io/p/sandbox/next-51030-hlj722?file=%2Fapp%2Fpage.tsx%3A1%2C1, it becomes very tricky if your CSS code itself has conflicts and some behaviors are undefined. For example when a dynamic component has loaded, its CSS might cause side effects to other parts of the page, which is expected.

For example, if you have a layout like this:

import style1 from 'a.module.css'
import style2 from 'b.module.css'

<button className={style1.btn + ' ' + style2.btn}>

The styles that imports later will override previous ones, so b.module.css will take precedence. However, if we have a page component that imports a.module.css again, it will take precedence over b.module.css again because page's styles have higher priority than the layout's. This is also a cause of duplicated styles.

In general, we recommend you to use CSS @layer, or try to make CSS selectors as pure and specific as possible.

@igordanchenko
Copy link

@shuding, I appreciate you taking the time to look into this example.

it becomes very tricky if your CSS code itself has conflicts and some behaviors are undefined.

I wouldn't call styles override a conflict. Overriding base component styles in a descendant component is a pretty common technique. Isn't that what "C" in CSS stands for?

For example when a dynamic component has loaded, its CSS might cause side effects to other parts of the page, which is expected.

The above example is a module-scoped CSS that doesn't have any global side-effects.

The styles that imports later will override previous ones, so b.module.css will take precedence.

Yes, you are absolutely correct, and that's the desired behavior that we are unfortunately not able to achieve under the app router.

However, if we have a page component that imports a.module.css again, it will take precedence over b.module.css again because page's styles have higher priority than the layout's. This is also a cause of duplicated styles.

In theory, yes. But it's not the case in the above example, isn't it?

@tinleym
Copy link

tinleym commented Jun 24, 2023

@shuding if you'll please read my comment below, @layer is not working.

The indeterministic order of imports wreaks havoc when CSS Cascade Layers are being used. The defined layer precedence that belongs in the root layout file gets read after the application of layers in child components. This causes the defined layer precedence to be ignored in favor of something random (https://css-tricks.com/css-cascade-layers/#aa-establishing-a-layer-order), and can make routes unusable.

This issue surfaced for me when updating up from 13.4.4 to 13.4.5 or 13.4.6.

@shuding
Copy link
Member

shuding commented Jun 24, 2023

Thanks! I'll go through all the reproductions and think about an improvement in the next couple of days!

I understand that the confusion caused by the new architecture. In general scoped CSS itself doesn't have conflicts, but the order and duplication might cause the styles to conflict (override each other). I'll give you an example:

layout: → layout.css
page: → page.css

If the module that contains the .btn class is imported lastly, it will override the styles defined in .base. If only layout is rendered, that's the expected behavior.

However when page is rendered, page.css will be loaded and it has only .base. This CSS file will occur after layout.css and override the .btn styles in that file.

Previously in the pages directory, all styles will be packed into one CSS file. But here we need to generate one CSS per layout and page because they can be loaded separately.

That said, we still have some ideas to improve it. Like changing the CSS modules hashing algorithm to make it base on the layer, or deduplicate styles that are already in its parent layer. Will keep you updated in this issue.

@boatilus
Copy link

That said, we still have some ideas to improve it. Like changing the CSS modules hashing algorithm to make it base on the layer, or deduplicate styles that are already in its parent layer.

If I understand the latter suggestion correctly, this seems to me like the right approach. If a build system produces hashed rule/class names based on the rule contents (e.g., ._129ac00b) in layout.css, it'd be unnecessary to duplicate that rule in page.css, and that duplication causes the unexpected precedence fighting. The basic idea here would be if (selector in layout.css) strip from page.css. Although I say that with that big caveat that I don't know the implications when overriding a rule generated in page.css that's intentional and how to signify that.

To your point, if there's perfect purity, this won't be a problem, but it's also reasonably unrealistic that there wouldn't be things like rules applied to descendent selectors (and probably some non-negigible number of other scenarios) on class names that have a high likelihood of their precedence being trumped by the same properties in a rule duplicated in page.css.

@sleepdotexe
Copy link
Contributor

sleepdotexe commented Jul 18, 2023

I'm also encountering the same behaviour.

For my use case, I'm creating standard UI/layout components that I want to use across multiple pages and components – these components have *.module.css styles. This allows me to add default custom stylings to these UI components and only load them if the component is used on the page. I also allow an additional className to be added for additional styling at a per-component level.

The problem is, as mentioned above, changing between routes/different app runs can result in different CSS load orders in addition to a huge amount of duplicate CSS.

For example:

// components/layout/Section.tsx

import styles from '../../styles/layout/Section.module.css';

interface Props extends React.ComponentProps<'section'> {
	fullWidth?: boolean;
}

export default function Section(props: Props) {
	const { fullWidth, ...remainingProps } = props;
	return (
		<section
			{...remainingProps}
			className={[styles['section'], props.className].join(' ')}
			data-fullwidth={fullWidth}
		>
			{props.children}
		</section>
	);
}

Navigating from Page A, to Page B, back to Page A cause Page A to display differently the second time round. Refreshing manually causes Page A to revert to its original layout.

For now, as a partial fix, I have managed to use css @layer to lower the CSS specificity of all the UI components' styles, however it still results in large amounts of duplicate css being added to each page, especially if the UI component is used frequently (such as a custom button).

Would be interested to know if there is a better way to go about this. I can provide more detailed examples/reproductions if necessary.

@jep-a
Copy link

jep-a commented Jul 21, 2023

@tinleym

The current order of imports wreaks havoc when CSS Cascade Layers are being used. The defined layer precedence that belongs in the root layout file gets read after the application of layers in child components. This causes the defined layer precedence to be ignored in favor of something random (https://css-tricks.com/css-cascade-layers/#aa-establishing-a-layer-order), and can make routes unusable.

This issue surfaced for me when updating up from 13.4.4 to 13.4.5 or 13.4.6.

Have a quick hack fix for the css layer order here #16630 (comment)

LeonardYam added a commit to LeonardYam/personal-portfolio that referenced this issue Aug 9, 2023
This reverts commit af98261.

Currently, importing 'normalize.css' is causing inconsistent styles (see: vercel/next.js#51030)
@katiasmet-93
Copy link

Is there already a solution for css that is loaded multiple times if you use the "use client" mode?
I use css modules and have f.e. several buttons or inputs on 1 page. The css (modules) are loaded multiple times in version 13.4.19.

@monolithed
Copy link

Is there a way to consolidate all styles into one file, so that the minifier automatically removes duplicates? I can't estimate the scale of the issue in kilobytes, but I see that each of my components (a total of 31) duplicates styles three times, and this is in production.

Screenshot 2023-09-05 at 01 37 57

@boatilus
Copy link

boatilus commented Sep 5, 2023

I just ended up writing a simple Webpack plugin that removes duplicate rules by their class names (which isn't a good idea in pratice, but has worked well enough for my needs). I've only tested this with CSS generated with Vanilla Extract, and it doesn't support route groups, but someone may find it useful.

@Stillonov
Copy link

Stillonov commented Sep 7, 2023

I have the same problems and it's awful. My app is very fragile.
It happens when I navigate through pages containing common components.

Screenshot 2023-09-08 at 01 24 15

@katiasmet-93
Copy link

Maybe not the best solution for now, but converting to the pages router fixed it for me.

@monolithed
Copy link

monolithed commented Sep 8, 2023

Maybe not the best solution for now, but converting to the pages router fixed it for me.

@katiasmet-93, your solution might be suitable for projects with a small codebase where there's no need for testing investments. But such thoughts really make one reconsider completely dropping Next.

@nikita2090
Copy link

I have the same problem when using shared components in layouts and pages (next 13.4.19). It doesn't work correctly both when using CSS modules and when using tailwind.

@olalonde
Copy link

Same issue with mantine #16630 (comment)

@khanakia
Copy link

khanakia commented Jul 5, 2024

@PASHANSX I also had the same issue I mentioned above what I did was move the CSS code out of the module and put it inside global.css

This may be how next.js 14 is designed which generated different CSS files for layout and page CSS; there is nothing you can do about this.

@aniketpadmansh
Copy link

aniketpadmansh commented Jul 5, 2024

Screenshot 2024-07-05 at 10 30 01 PM

un necessary css is being bundled with each page

@Netail
Copy link
Contributor

Netail commented Jul 5, 2024

Screenshot 2024-07-05 at 10 30 01 PM

un necessary css is being bundled with each page

Those are chunks no? It basically fetches css on demand

@aniketpadmansh
Copy link

I am using module.scss
so scss writtn in b and c and not used on page a is also getting downloaded for page a

@kotAPI
Copy link

kotAPI commented Jul 8, 2024

There is no problem when building, but the problem occurs only when using dev. When can it be fixed? This is making it very difficult for me.

@dfd1123 Can you share which :nextjs: versions you have tested? An what exact CSS issue you are seeing in next dev?

@kotAPI Your previous post's reply implies you're not on a later version with the first fix for ordering/merging—that's why you're not able to use cssChunking: 'strict'. Are you able to upgrade to the latest 14 canary or the the current latest canary version?

hey @samcx , thanks for the response.

Here's the docs website that runs on Next 14 - https://github.com/rad-ui/website
It's currently on 14.1, will this work?

@samcx
Copy link
Member

samcx commented Jul 8, 2024

@kotAPI You need to be at least on v14.2.0-canary.28—14.1 would not have that this :pr:#63157.

@kotAPI
Copy link

kotAPI commented Jul 8, 2024

@kotAPI You need to be at least on v14.2.0-canary.28—14.1 would not have that this :pr:#63157.

Ok, thanks for that, will upgrade and revert back if this fixes the issue 🤞

@christian-tchaikovsky
Copy link

christian-tchaikovsky commented Jul 8, 2024

Ok back with a report!

import * as React from "react";
import "./global.css"; // switched this to be above the Component, so the global css comes first
import { Component } from "./Component";

export default function Layout(props: React.PropsWithChildren) {
  return (
    <html lang="en">
      <body>
        {props.children}
        <Component />
      </body>
    </html>
  );
}

This :pr: #67373 is going to land as well, which fixes #64773. This may fix some of the other :repro: I may have missed.

So far, I think the pressing issues that are actively known with confirmed :repro:s are:

  1. Server ↔ Client components are not respecting CSS import order
  2. CSS is leaking between Layouts in Route Groups (getting :fixed: by avoid merging global css in a way that leaks into other chunk groups #67373). Not 100% sure if avoiding merging the global.css will fix other issues, but will be taking a look!

@samcx, Hi

Updated the Next version.js is up to the latest version and noticed that duplicates occur if the component is used in both page.tsx and layout.tsx

If a component is used in multiple layout.tsx, then the number of duplicates corresponds to the number of uses in layout.tsx

Here is an example

Glad to help

@AndrePessoa
Copy link

@samcx, first, thank you for your amazing work!

Second, the experiment flag worked for me on my team project and it seems to be helping more people. Do you plan to release it as a patch any time soon?

@Netail
Copy link
Contributor

Netail commented Jul 10, 2024

Server ↔ Client components are not respecting CSS import order

Actually is seems more like the following;

If the layout uses the test component and passes a override class to the object, but a page also uses the test component.
The style sheets for both will have the test component's css imported with the same class names, but as the page scss is injected into the html after the layout scss, the default css class will be used last, thus resetting the override css

Screenshot 2024-07-10 at 15 22 40 Screenshot 2024-07-10 at 15 22 15

@samcx
Copy link
Member

samcx commented Jul 10, 2024

Updated the Next version.js is up to the latest version and noticed that duplicates occur if the component is used in both page.tsx and layout.tsx; If a component is used in multiple layout.tsx, then the number of duplicates corresponds to the number of uses in layout.tsx

@Christopher-md Can you try minimizing your :repro:? I don't see examples for either cases.

@AndrePessoa No plan yet. I think ideally, this shouldn't even be an option—we should be able to minimize the number of requests (merging properly) without breaking CSS. There was an additional fix (also just backported to 14.2.5), which may allow for not having to use the experimental option.

@Netail Is this showcased in your :repro: in your other reported issue? :frog-eyes:

@Netail
Copy link
Contributor

Netail commented Jul 10, 2024

@Netail Is this showcased in your :repro: in your other reported issue? :frog-eyes:

Yes! That's the example used :)

@kutsan
Copy link
Contributor

kutsan commented Jul 11, 2024

@samcx I'm still having the problem, even with the v14.2.5 release. Additionally tested this in 15.0.0-canary.63 with no luck.

Here's my repro: https://github.com/kutsan/nextjs-css-duplicates-issue

When I inspect the title styles, I'm seeing duplicates.

So far, I've found two possible solutions:

  1. In @/app/layout.tsx, exporting from @/features/RootLayout instead of @/features fixes it and removes the duplicate styles.
  2. Removing 'use client' in the @/features/RootRoute.tsx file also fixes it.

I'm not sure why these work, but I hope this helps.

image

@Netail
Copy link
Contributor

Netail commented Jul 12, 2024

Removing 'use client' in the @/features/RootRoute.tsx file also fixes it.

Might be the same issue I am facing. Also related to the "use client" directive

@christian-tchaikovsky
Copy link

Updated the Next version.js is up to the latest version and noticed that duplicates occur if the component is used in both page.tsx and layout.tsx; If a component is used in multiple layout.tsx, then the number of duplicates corresponds to the number of uses in layout.tsx

@Christopher-md Can you try minimizing your :repro:? I don't see examples for either cases.

@AndrePessoa No plan yet. I think ideally, this shouldn't even be an option—we should be able to minimize the number of requests (merging properly) without breaking CSS. There was an additional fix (also just backported to 14.2.5), which may allow for not having to use the experimental option.

@Netail Is this showcased in your :repro: in your other reported issue? :frog-eyes:

@samcx

Here is a minimized RE-PRO

@Netail
Copy link
Contributor

Netail commented Jul 18, 2024

I have not words.

Was developing my whole app for a month with --turbo relying on CSS cascades heavily just to found out it's completely broken after the build

Is this the DX improvements we were promised with Turbo (which no one asked for)?

Turbopack is only ready for development, production still has 537 test remaining to be fixed (https://areweturboyet.com/build). Most of the remaining tests are related to CSS

@bruno12mota
Copy link

bruno12mota commented Aug 2, 2024

I have not words.
Was developing my whole app for a month with --turbo relying on CSS cascades heavily just to found out it's completely broken after the build
Is this the DX improvements we were promised with Turbo (which no one asked for)?

Turbopack is only ready for development, production still has 537 test remaining to be fixed (https://areweturboyet.com/build). Most of the remaining tests are related to CSS

It is happening in turbo development though, I've opened a new issue #68412 since it might not be entirely related with this one and has a minimal repo to showcase the issue.

@pixelchutes
Copy link

pixelchutes commented Aug 2, 2024

Following. I believe I am also running into this. Even with the merge of #67373, when using --turbo on v14.2.5, I am seeing CSS Module specific issues, primarily local dev UI is not matching production. When turbo dev is disabled, we are still seeing differences between dev + prod, which in our case seems to be directly related to use of CSS Modules.

Further, with --turbo + CSS Modules, HMR is throwing console errors for any changes within .module.css:

Error: No link element found for chunk static/chunks/_793f98._.css

image

@samcx
Copy link
Member

samcx commented Aug 2, 2024

@pixelchutes Is it possible to create a minimal :repro: so we can take closer a look at your case?

@pixelchutes
Copy link

@samcx Let me see if I can put something together! Creating a reminder to revisit early next week 👍🏼

@subproject22
Copy link

@samcx I think Paco made a perfect repro of this issue before, it was ignored for some reason. Here's is it, did you saw it? https://github.com/pacocoursey/next-cascade-layers

@kotAPI
Copy link

kotAPI commented Aug 5, 2024

@samcx I think Paco made a perfect repro of this issue before, it was ignored for some reason. Here's is it, did you saw it? https://github.com/pacocoursey/next-cascade-layers

This is a great example!

@Netail
Copy link
Contributor

Netail commented Aug 7, 2024

Are there any internally discussed updates we should know of? CSS is a pretty crucial part 😥

@samcx
Copy link
Member

samcx commented Aug 8, 2024

@subproject22 Paco's example wasn't ignored. There was actually no broken behavior here, we just had to update the import order, which matters. I shared this here (earlier comment) → #51030 (comment).

@Netail Nothing to share yet. Will be considering moving this to discussions soon, as this thread is becoming too broad (everyone is just piling their CSS issue here)—we need to actually isolate an issue to the actual specifics at play, such as your Client ↔ Server CSS issue that you reported.

We are also tackling this old CSS issue as well → #33286.

@Enkratia
Copy link

Enkratia commented Aug 8, 2024

@samcx i created default next.js app with 1 server component, 1 LIink, 1 Image, 1 css module. Can you explain why HMR doesn't work? This problem persists more than 1 year.

If repro is needed: https://github.com/Enkratia/css-reload-show

@samcx
Copy link
Member

samcx commented Aug 8, 2024

@Enkratia Please open a separate bug report detailing your HMR issue with CSS.

@vercel vercel locked and limited conversation to collaborators Aug 8, 2024
@samcx samcx converted this issue into discussion #68684 Aug 8, 2024

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
area: app App directory (appDir: true) bug Issue was opened via the bug report template. linear: next Confirmed issue that is tracked by the Next.js team.
Projects
None yet
Development

No branches or pull requests