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

SCSS modules cannot load images correctly with next-images package #11164

Closed
gilian-schreuders opened this issue Mar 18, 2020 · 17 comments
Closed
Labels
Upstream Related to using Next.js with a third-party dependency. (e.g., React, UI/icon libraries, etc.).

Comments

@gilian-schreuders
Copy link

gilian-schreuders commented Mar 18, 2020

Update: Read my other comments below for a more pinpointed problem.

Bug report

Describe the bug

I tried to set a background image in a SCSS module with background: url("./footer-region-bg.jpg") repeat; (is in the same directory as the component). But this results in the following when fetching the image.

image

This was working with @zeit/next-sass, but it doesn't with the native scss modules.

To Reproduce

Load an Image from the same directory as the component in an SCSS module like mentioned above. Use the native SCSS modules from NextJs.

Expected behavior

The image should be loaded correctly

System information

  • OS: Windows
  • Version of Next.js: 9.3.1
@gilian-schreuders
Copy link
Author

Sone new things I tried:

Didn't work:

  • Remove .next directory
  • Changing back to next 9.3.0

What worked:

  • Created a new Next.js project simulating the same scenario

I will now check why it works in a new project but not in my project

@gilian-schreuders
Copy link
Author

gilian-schreuders commented Mar 18, 2020

Alright, after some more testing I found something interesting. The problem can be reproduced when using the next-images package. My current next.config.js looks like this:

const withImages = require("next-images");

module.exports = withImages({
	poweredByHeader: false // This doesn't really matter
});

For some reason this config breaks the SCSS/CSS module images resolver.

Update: The same is true for the https://www.npmjs.com/package/next-optimized-images package.

@gilian-schreuders gilian-schreuders changed the title SCSS modules cannot load images correctly SCSS modules cannot load images correctly with next-images package Mar 18, 2020
@ckken
Copy link

ckken commented Mar 18, 2020

same for me

@y2x33
Copy link

y2x33 commented Mar 19, 2020

Same here.
When I remove my url-loader related code in next.config.js, background-image works fine.
There's my code :

// next.config.js
// ....
    config.module.rules.push(
      {
        test: /\.(jpg|png|gif|ico)$/,
        use: [
          {
            loader: "url-loader"
          }
        ]
      }
    );
// ...

@theshem
Copy link

theshem commented Mar 22, 2020

I'm not sure if it is a next-images issue or the Next.js itself.

I faced the same issue after upgrading from 9.1.7 to 9.3.1. I was using next-sass and url-loader with the custom webpack config.

By using the built-in Sass feature, background and font face url()s inside .scss files stop working. After digging into Next.js files I realized that it handles url() imports through the file-loader now.

https://github.com/zeit/next.js/blob/v9.3.1/packages/next/build/webpack/config/blocks/css/index.ts#L278-L292

https://github.com/zeit/next.js/blob/v9.3.1/packages/next/build/webpack/config/blocks/css/index.ts#L17

I hadn't used the built-in CSS feature before and that is why I hadn't faced the issue.

Workaround

I ended up with excluding the url() file imports by setting a test regex in webpack issuer config as follows:

// next.config.js
module.exports = {
  // ...
webpack: (config) => {
    config.module.rules.push({
      issuer: {
        // nextjs already handles url() in css/sass/scss files
        test: /\.\w+(?<!(s?c|sa)ss)$/i,
      },
      test: /\.(jpg|gif|png|svg|ico)$/i,
      use: [
        {
          loader: 'url-loader',
          options: {
            // sample options
            limit: 8192,
            outputPath: '...',
            context: 'src',
            name: '[path][name].[hash:8].[ext]',
            publicPath: `your/public/path`,
          },
        },
      ],
    });

    return config;
  },
  // ...
};

Hope this helps.

@tomdohnal
Copy link
Contributor

I'm not sure if it is a next-images issue or the Next.js itself.

I faced the same issue after upgrading from 9.1.7 to 9.3.1. I was using next-sass and url-loader with the custom webpack config.

By using the built-in Sass feature, background and font face url()s inside .scss files stop working. After digging into Next.js files I realized that it handles url() imports through the file-loader now.

https://github.com/zeit/next.js/blob/v9.3.1/packages/next/build/webpack/config/blocks/css/index.ts#L278-L292

https://github.com/zeit/next.js/blob/v9.3.1/packages/next/build/webpack/config/blocks/css/index.ts#L17

I hadn't used the built-in CSS feature before and that is why I hadn't faced the issue.

Workaround

I ended up with excluding the url() file imports by setting a test regex in webpack issuer config as follows:

// next.config.js
module.exports = {
  // ...
webpack: (config) => {
    config.module.rules.push({
      issuer: {
        // nextjs already handles url() in css/sass/scss files
        test: /\.\w+(?<!(s?c|sa)ss)$/i,
      },
      test: /\.(jpg|gif|png|svg|ico)$/i,
      use: [
        {
          loader: 'url-loader',
          options: {
            // sample options
            limit: 8192,
            outputPath: '...',
            context: 'src',
            name: '[path][name].[hash:8].[ext]',
            publicPath: `your/public/path`,
          },
        },
      ],
    });

    return config;
  },
  // ...
};

Hope this helps.

Works perfectly. I looked in the source code of the next-images and it basically includes a url-loader with a predefined configuration for you. I created an issue in their repository and I think that your fix should work there.

@Timer
Copy link
Member

Timer commented Mar 23, 2020

This is a bug with the next-images package(s), please file them upstream.

tl;dr the new CSS support does not require next-images, so next-images needs to be sure to only apply to JS files. There could be an option to "force on" for CSS files for legacy use cases.

@Timer Timer added the Upstream Related to using Next.js with a third-party dependency. (e.g., React, UI/icon libraries, etc.). label Mar 23, 2020
@arefaslani
Copy link

Thanks guys for reporting this issue. I'll fix that in the next-images package today and will release a new patch for that.

@theshem
Copy link

theshem commented Mar 23, 2020

@Timer Thank you. As an aside, it would be great if we can choose between file-loader and url-loader for url()s inside CSS/Sass files (or even setting the options, perhaps in the next.config.js?) to take advantage of the Data-URI thing.

@Timer
Copy link
Member

Timer commented Mar 23, 2020

@theshem Can you open a new issue proposing to add support for data URIs OOTB?

One thing to note, using data URIs are not generally a safe default because it's incompatible with SVG sprite systems, for example.

If we were to use data URIs out of the box (for small assets), SVGs would need to be excluded:
https://css-tricks.com/svg-fragment-identifiers-work/

@ajoslin103
Copy link

for reference: twopluszero/next-images#31

@arefaslani
Copy link

Thanks to @theshem, this issue is solved now in next-images.

@Timer
Copy link
Member

Timer commented Mar 29, 2020

Closing as it was solved upstream. Thanks everyone!

@Timer Timer closed this as completed Mar 29, 2020
@bericp1
Copy link

bericp1 commented Mar 29, 2020

The "fix" here appears to be mostly a workaround though. It would be nice if Next detected the use of image processing plugins like next-images or next-optimized-images, particularly the latter. Right now there's a working workaround for next-optimized-images but it doesn't allow you to run images imported in SCSS through i.e. optipng etc.

This might still be an upstream issue (some way to tie into Next's handling of images to apply optimizations?) but it seems like there's precedent here for the "detect and disable" functionality for handling images since that's what happens right now i.e. if you're using next-sass. From the official 9.3 blog post:

This new feature is fully backwards compatible. If you are using @zeit/next-sass or other CSS related plugins the feature is disabled to avoid conflicts.

I realize this issue mentions next-images specifically in the title, let me know if I should open a new issue instead.

@zobzn
Copy link

zobzn commented Apr 9, 2020

The "fix" doesn't solve importing images into js files through template literals.

Given the src/styles/index.module.css file:

.test::before {
  content: url("./images/image.jpg");
}

Example 1

no next.config.js

src/pages/index.js:

import styles from "../styles/index.module.css";
export default function IndexPage() {
  return (
    <div>
      {/* this works */}
      <div className={styles.test}></div>
      {/* this doesn't work */}
      <div><img src={require('../styles/images/image.jpg')} /></div>
      {/* this doesn't work */}
      <div>{["image.jpg"].map((src) => (<img key={src} src={require(`../styles/images/${src}`)} />))}</div>
    </div>
  );
}

Example 2

next.config.js:

module.exports = {
  webpack: (config, options) => {
    config.module.rules.push({
      test: /\.(png|gif|jpg|jpeg)(\?|$)/,
      use: [
        {
          loader: "file-loader",
          options: {
            publicPath: `/_next/static/media/`,
            outputPath: `${options.isServer ? "../" : ""}static/media/`,
            name: "[name].[hash].[ext]",
          },
        },
      ],
    });

    return config;
  },
};

src/pages/index.js:

import styles from "../styles/index.module.css";
export default function IndexPage() {
  return (
    <div>
      {/* this doesn't work */}
      <div className={styles.test}></div>
      {/* this works */}
      <div><img src={require('../styles/images/image.jpg')} /></div>
      {/* this works */}
      <div>{["image.jpg"].map((src) => (<img key={src} src={require(`../styles/images/${src}`)} />))}</div>
    </div>
  );
}

Example 3

next.config.js:

module.exports = {
  webpack: (config, options) => {
    config.module.rules.push({
      test: /\.(png|gif|jpg|jpeg)(\?|$)/,
      issuer: {
        exclude: /\.(css|sass|scss)$/i,
      },
      use: [
        {
          loader: "file-loader",
          options: {
            publicPath: `/_next/static/media/`,
            outputPath: `${options.isServer ? "../" : ""}static/media/`,
            name: "[name].[hash].[ext]",
          },
        },
      ],
    });

    return config;
  },
};

src/pages/index.js:

import styles from "../styles/index.module.css";
export default function IndexPage() {
  return (
    <div>
      {/* this works */}
      <div className={styles.test}></div>
      {/* this works */}
      <div><img src={require('../styles/images/image.jpg')} /></div>
      {/* this doesn't work */}
      <div>{["image.jpg"].map((src) => (<img key={src} src={require(`../styles/images/${src}`)} />))}</div>
    </div>
  );
}

So what should I do to get all three this works together ?

@orxanaliyev
Copy link

Solved!

style.module.scss

.Home-Page-Header {
  border: 1px solid red;
  background-image: url("/static/images/header.jpg");
}

next.config.js

module.exports = {

  webpack: (config) => {
    config.module.rules.push({
      test: /\.(png|jpg|gif)$/i,
      use: [
        {
          loader: "url-loader",
          options: {
            limit: 8192,
          },
        },
      ],
    });

    return config;
  },

};

@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 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Upstream Related to using Next.js with a third-party dependency. (e.g., React, UI/icon libraries, etc.).
Projects
None yet
Development

No branches or pull requests