Skip to content
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

CSS Modules sometimes don't load when using next/dynamic import #33286

Closed
jzxhuang opened this issue Jan 13, 2022 · 28 comments · Fixed by #72959
Closed

CSS Modules sometimes don't load when using next/dynamic import #33286

jzxhuang opened this issue Jan 13, 2022 · 28 comments · Fixed by #72959
Labels
bug Issue was opened via the bug report template. locked Webpack Related to Webpack with Next.js.

Comments

@jzxhuang
Copy link

Run next info (available from version 12.0.8 and up)

Operating System:
  Platform: darwin
  Arch: x64
  Version: Darwin Kernel Version 20.3.0: Thu Jan 21 00:06:51 PST 2021; root:xnu-7195.81.3~1/RELEASE_ARM64_T8101
Binaries:
  Node: 14.17.0
  npm: 6.14.13
  Yarn: 1.22.10
  pnpm: N/A
Relevant packages:
  next: 12.0.8
  react: 17.0.2
  react-dom: 17.0.2

What version of Next.js are you using?

12.0.8

What version of Node.js are you using?

14.17

What browser are you using?

Chrome

What operating system are you using?

macOS

How are you deploying your application?

Vercel

Describe the Bug

When loading a component using next/dynamic, CSS modules are sometimes not loaded correctly. This seems like it may be similar to #18252, which is described as fixed (although there is no linked PR showing the fix).

This bug only occurs when running next build + next start. Works as expected in development (next dev). Additionally, there seem to very specific scenarios where it occurs, which I've tried to outline as clearly and concisely as possible int he repro steps.

Expected Behavior

CSS modules should be loaded into the document so that your styles are applied.

To Reproduce

Repro Repo and Deployment

I've created a demo using create-next-app --typescript 12.0.8: https://github.com/jzxhuang/dynamic-import-css-module-next-bug

It's hosted at: https://dynamic-import-css-module-next-bug.vercel.app/

Isolated Reproduction Steps

  1. Go directly to https://dynamic-import-css-module-next-bug.vercel.app/broken/one, notice the button has a red background.
  2. Click the link to /foo. The button will now have a clear background, even though it is the exact same component with no changes whatsoever.

This can be reproduced with both SCSS and CSS modules. Repeating the above steps with https://dynamic-import-css-module-next-bug.vercel.app/broken/two will break the blue button. This is reproduced only in the "production" build, if you follow the same steps with next dev, the buttons on /foo should always be the correct color.

Cases where it works, but why?

The above steps are an isolated reproduction of this bug which we found in production. Now, here's where it starts getting super weird, we decided to do some more experimentation. I'm not sure if the following scenarios will be helpful or just throw you into a wild goose chase, but here we go:

  1. Go to https://dynamic-import-css-module-next-bug.vercel.app
  2. Click the link to /broken/one, DO NOT REFRESH
  3. Click the link to /foo.
  4. It's expected that the button is now red...

We also created a few other examples, under the routes /working/*. The only difference here is that we either load 2, 3, or 0 of the buttons. For some reason, these seems to work fine, but if you load only one button, as shown in broken/one and broken/two, the styling will be broken.

If you don't lazy load, everything is fine

Back to something that makes sense. The example /working/three shows that the blue button will always be the correct color. It is not dynamically imported in /foo, and thus does not have the same bug that the dynamically imported buttons do.

What's going on in the DOM?

On /broken/one, we see the button has the class comp-one_button__MtA0_ which is in the stylesheet 0ff09bacf4af1553.css.
image

When we click into /foo, the button still has the same class, but the stylesheet is not being applied.
image

I'm not sure what the root cause of this might be, but there's somehow a stylesheet missing when dynamically importing, and it seems to be related to using a component on the previous component inside the dynamically imported component.

@jzxhuang jzxhuang added the bug Issue was opened via the bug report template. label Jan 13, 2022
@jzxhuang
Copy link
Author

Hey, wondering if there are any insights or leads on this one! It's affecting us in production and we don't have any known workarounds — next/dynamic is required for to avoid a problematic library import that is not SSR-compatible. Thanks!

@balazsorban44 balazsorban44 added the Webpack Related to Webpack with Next.js. label Jan 20, 2022
@skumpulainen
Copy link

Having similar issues with missing css files!

@PlopTheReal
Copy link

bump, same issue on nextjs 12.1.5

@khujay15
Copy link

khujay15 commented May 3, 2022

this looks similar to #17464 , which is about nextJs removing preloaded css.

there're some solutions to solve this issue, but kind of hacky way.

#17464 (comment)

@sjohnston88
Copy link

Also seeing this on NextJS 12.2.2. Can confirm that the issue goes away if not using dynamic imports.

@sjohnston88
Copy link

The fix mentioned in #17464 has worked for me as a temporary workaround:

// In _app.tsx
const router = useRouter();

useEffect(() => {
  const handleRouteChange = () => {
    const styleElements = document.querySelectorAll('style[media="x"]');
    styleElements.forEach(styleTag => {
      styleTag.removeAttribute('media');
    });
  };

  router.events.on('routeChangeComplete', handleRouteChange);
  router.events.on('routeChangeStart', handleRouteChange);
}, [router]);

@shaianest
Copy link

the issue persist on next 12.2.4 and was not a thing for a long time and suddenly appeared.

@hc0503
Copy link

hc0503 commented Mar 21, 2023

This issue is happened in AppDir beta version of NextJS 13 as well.

@simonxdev
Copy link

Having the same Issue right now using NextJS 13. Any News here ?

@Qavi-Nizamani
Copy link

Having the same issue with Next.js 13, when navigating to any other page from the Home page, the CSS doesn't work.

@mFirghi
Copy link

mFirghi commented May 9, 2023

Any lead on this so far, or any workarounds? It seems that it only happens on production after deployment for me.

@simonxdev
Copy link

In my Case, im building a Site with Data coming from a CMS. So i wanted to load the needed Components to the currently active Sites with Next/dynamic.

I tried all kind of stuff, but in the End solved the Issue with a switch. Guess we have to wait for a future release to get things fixed with dynamic. For now i would only recommend to use it with small components that dont load extra CSS.

Heres an Example how i did it, like this the Components will be returned the normal way and CSS Modules will be loaded:

          `{components.map((component: any, k: any) => {
              if (componentsProps[k].componentregion === regionNum) {

                switch (component) {
                  case "Example_component1":
                    return (
                      <Example_component1
                        componenttype={componentsProps[k].componenttype}
                        componentid={componentsProps[k].componentid}
                        key={k}
                      />
                    )
                  break;
                  case "Example_component2":
                    return (
                      <Example_component2
                        componenttype={componentsProps[k].componenttype}
                        componentid={componentsProps[k].componentid}
                        key={k}
                      />
                    )
                  break;
                  case "Example_component3":
                    return (
                      <Example_component3
                        componenttype={componentsProps[k].componenttype}
                        componentid={componentsProps[k].componentid}
                        key={k}
                      />
                    )
                  break;
                  case "Example_component4":
                    return (
                      <Example_component4
                        componenttype={componentsProps[k].componenttype}
                        componentid={componentsProps[k].componentid}
                        key={k}
                      />
                    )
                  break;
                }
              }
            })}`

@stormsson
Copy link

stormsson commented May 23, 2023

Same issue here :( v.13.4.3

@danmondra
Copy link

Same issue in 13.4.4

danmondra added a commit to danmondra/Hirenix that referenced this issue May 27, 2023
@stormsson
Copy link

opened repro scenario here #50300

@Luderio
Copy link

Luderio commented Jul 12, 2023

Same issue with next.js 13.4.7

@BowlingX
Copy link

BowlingX commented Aug 3, 2023

A possible workaround for this problem is to move all async chunks into one group.

next.config.mjs:

export default {
  webpack(config, context) {
    const { isServer, dev } = context
    if(!isServer && !dev) {
       config.optimization.splitChunks.cacheGroups.asyncChunks = {
         enforce: true,
         type: "css/mini-extract",
         chunks: 'async',
       }
    }
   return config
  }
}

@popmatik
Copy link

Same issue here next.js v 13.4.19

@r742davis
Copy link

A possible workaround for this problem is to move all async chunks into one group.

next.config.mjs:

export default {
  webpack(config, context) {
    const { isServer, dev } = context
    if(!isServer && !dev) {
       config.optimization.splitChunks.cacheGroups.asyncChunks = {
         enforce: true,
         type: "css/mini-extract",
         chunks: 'async',
       }
    }
   return config
  }
}

Has anyone had luck with this solution? I have not tried it yet in production but it seems promising locally when removing !dev in the if statement

@Krasnopir
Copy link

Krasnopir commented Nov 23, 2023

A possible workaround for this problem is to move all async chunks into one group.
next.config.mjs:

export default {
  webpack(config, context) {
    const { isServer, dev } = context
    if(!isServer && !dev) {
       config.optimization.splitChunks.cacheGroups.asyncChunks = {
         enforce: true,
         type: "css/mini-extract",
         chunks: 'async',
       }
    }
   return config
  }
}

Has anyone had luck with this solution? I have not tried it yet in production but it seems promising locally when removing !dev in the if statement

It does not work for me.


  const handleRouteChangeComplete = useCallback((page: string) => {
    ga.pageView(page);
    applyPreloadStylesheetLinks();
  }, []);

useEffect(() => {
    router.events.on('routeChangeComplete', handleRouteChangeComplete);

    return () => {
      router.events.off('routeChangeComplete', handleRouteChangeComplete);
    };
  }, [handleRouteChangeComplete, router.events]);

where

const applyPreloadStylesheetLinks = () => {
  const preloadStylesheetLinks = document.querySelectorAll<HTMLLinkElement>("link[rel='preload'][as='style']");
  preloadStylesheetLinks.forEach((preloadLink) => {

    const existingStylesheetLink = document.querySelector<HTMLLinkElement>(
      `link[rel='stylesheet'][href='${preloadLink.href}']`,
    );


    if (!existingStylesheetLink) {
      const stylesheetLink = document.createElement('link');
      stylesheetLink.rel = 'stylesheet';
      stylesheetLink.href = preloadLink.href;
      document.head.appendChild(stylesheetLink);
    }
  });
};

it works

@alexgilbertDG
Copy link

This is still an open issue for us !

@stefanmkodo

This comment has been minimized.

@topolanekmartin

This comment has been minimized.

@juansaav

This comment has been minimized.

@categoricalcat

This comment has been minimized.

@li4man0v
Copy link

li4man0v commented Jul 8, 2024

I’ve been looking into this issue for quite a while and found that my case is exactly like the one described in this GitHub issue. It’s a bit surprising (and a little frustrating) that CSS modules aren’t fully usable yet.

I came up with a workaround to fetch styles dynamically, and I thought I’d share it here in case it helps someone else:

import dynamic from 'next/dynamic';
import React from 'react';

const StyleProvider = dynamic(
  () =>
    import('styles/FancyStyles.module.css').then((module) => {
      interface ComponentProps {
        children: (style: typeof module.default) => JSX.Element;
      }
      const Component: React.FC<ComponentProps> = ({ children }) =>
        children(module.default);
      return Component;
    }),
  {
    loading: () => <p>Loading...</p>,
  }
);
const FancyComponent: React.FC = () => (
  <StyleProvider>
    {(style) => <SomeComponent className={style.fancy} />}
  </StyleProvider>
);

This solution works for now, but it definitely feels more like a stopgap than a permanent fix. If anyone has a better approach or any insights on how to handle this more effectively, I’d really appreciate your input!

@devjiwonchoi
Copy link
Member

devjiwonchoi commented Nov 30, 2024

Hey everyone, sorry for the long delay, and thank you for your patience.

The fix has been landed at PR #72959, release v15.0.4-canary.32, so feel free to try it, and let me know if it doesn't work for you. We are expecting to release this in the next release. Thank you!

Copy link
Contributor

This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Issue was opened via the bug report template. locked Webpack Related to Webpack with Next.js.
Projects
None yet