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

Nextjs - Dynamic Imports - CSS Modules cannot be imported from within node_modules #31271

Closed
Legym opened this issue Nov 10, 2021 · 2 comments
Closed
Labels
bug Issue was opened via the bug report template.

Comments

@Legym
Copy link

Legym commented Nov 10, 2021

What version of Next.js are you using?

12.0.3

What version of Node.js are you using?

14.17.3

What browser are you using?

Chrome and Firefox

What operating system are you using?

Windows and MacOS

How are you deploying your application?

npm run dev

Describe the Bug

Struggling to wrap my head around this issue. I am using the built in CSS modules for my components in Nextjs. When i lazy load my component that has a CSS module, I get the error CSS Modules cannot be imported from within node_modules..

If I replace ${asset.name} with the value button, i.e dynamic(() => import(../src/component/${asset}), nextjs will compile.

  1. The code will execute correctly if i dont use CSS modules. It will lazy load dynamic components in. Only when i add the file to the project.
  2. If i hardcode the import path it compiles. I only have this issue when I use a template literal string.
import React from 'react';
import dynamic from 'next/dynamic';

const asset = { name: 'Button' };
const NewComponent = dynamic(() =>import(`../src/component/${asset}`), {
    ssr: false,
});

export default function index() {
    return (
    <div className="grid-container">
        <div className="grid-x grid-margin-x">
        <div className="cell medium-6 large-6">
            <NewComponent />
        </div>
        <div className="cell medium-6 large-6">12/6/8 cells</div>
        </div>
    </div>
    );
}

Button.js

import React from 'react';
import styles from './Test.module.css';

export default function Index() {
  return <div className={styles['btn-primary']}>Test Div</div>;
}

If I move the Test.module.css into a different folder, it will compile.
I havent seen any documentation or reasoning why the CSS modules has to live in a particular area.

button.js updated

import React from 'react';
import styles from '~/test/Test.module.css'; // <-- Moved into a different folder

export default function Index() {
  return <div className={styles['btn-primary']}>Test Div</div>;
}

Expected Behavior

I should be able to dynamically import my component that uses css modules

To Reproduce

Here is a demo of the issue.

https://github.com/Legym/nextjs-css-modules/tree/master

@Legym Legym added the bug Issue was opened via the bug report template. label Nov 10, 2021
@Legym
Copy link
Author

Legym commented Nov 11, 2021

I wanted to post an update. Spent the entire day trying to understand what was happening. I missed this in the docs. That was my mistake.

image

dynamic(() =>import(../src/component/${asset}) does work and will lazy load react components. However CSS modules did not like this at all and this is what my error was about.

Since I know the CSS modules was working if I didnt use template literals, I needed a different solution to lazy load my components. I ended up writing this in my next.config.js

module.exports = {
  webpack: (config, { isServer }) => {
    // Setup alias
    config.resolve.alias['@preamp'] = path.resolve(currentPath, 'preamp/');

    // Required so nextjs can read .html files the CDK. This is required for editable assets
    config.module.rules.push({
      test: /\.html$/i,
      loader: 'html-loader',
    });

    if (isServer) {
      // Check which folder to run through
      const directoryPath = path.join(__dirname, 'preamp/assets');

      fs.readdir(directoryPath, (err, files) => {
        const discoveredAssets = [];

        // handling error
        if (err) {
          return console.log(`Unable to scan directory: ${err}`);
        }

        // listing all files using forEach
        files.forEach((asstName) => {
          discoveredAssets.push(asstName);
        });

        fs.writeFileSync(path.join(__dirname, 'FOLDER', 'FILENAME.js'), template(discoveredAssets), 'UTF8');

        console.log('Finish Building List');
      });
    }

    return config;
  },
}

Rather than trying to load the assets at runtime, i'm generating a hash table that has my dynamic import with already declared imports whenever i start a build or run npm run dev. I opted in to use webpack to compile my new list, but a node script could have worked as well.

I end up getting my hash like this:
image

So my final component that does the lazy loading looks like this.

const dynamicComponents = {
  About: dynamic(() => import("./path/to/about")),
  Other: dynamic(() => import("./path/to/other")),
  ...
};

// ... in your page or whatever
const Component = dynamicComponents[subComponent];
return <Component />

and now my CSS modules work with my lazy loaded components. :)

@Legym Legym closed this as completed Nov 11, 2021
@balazsorban44
Copy link
Member

This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@vercel vercel locked as resolved and limited conversation to collaborators Jan 27, 2022
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.
Projects
None yet
Development

No branches or pull requests

2 participants