next.js: SVGR fails to load SVGs with Next 11
UPDATE
A temporary solution that seems to work and does not disable Webpack 5
If you find any issues with this solution, please tag me @scottagirs to update as appropriate. By @dohomi
webpack (config) {
const fileLoaderRule = config.module.rules.find(rule => rule.test && rule.test.test('.svg'))
fileLoaderRule.exclude = /\.svg$/
config.module.rules.push({
test: /\.svg$/,
loader: require.resolve('@svgr/webpack')
})
return config
}
What version of Next.js are you using?
Next 11.0.0
What version of Node.js are you using?
14.x
What browser are you using?
Chrome, Brave
What operating system are you using?
macOS 11.4
How are you deploying your application?
localhost atm
Describe the Bug
https://discord.com/channels/752553802359505017/752668543891276009/854396809497673788
After upgrading from 10.x Next.js with webpack 4 config, to Nextjs 11, application fails to build with the below errors:
error - ./icon.svg
TypeError: unsupported file type: undefined (file: undefined)
Full stack trace when trying to load the app (open in browser tab)
<span>----- Expand to view -----</span>
Error: Cannot find module '/Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/.next/server/pages-manifest.json'
Require stack:
- /Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/next-server/server/require.js
- /Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/next-server/server/load-components.js
- /Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/next-server/server/api-utils.js
- /Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/next-server/server/next-server.js
- /Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/server/next.js
- /Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/server/lib/start-server.js
- /Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/cli/next-dev.js
- /Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/bin/next
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:880:15)
at Function.mod._resolveFilename (/Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/build/webpack/require-hook.js:4:1855)
at Function.Module._load (internal/modules/cjs/loader.js:725:27)
at Module.require (internal/modules/cjs/loader.js:952:19)
at require (internal/modules/cjs/helpers.js:88:18)
at getPagePath (/Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/next-server/server/require.js:1:735)
at requirePage (/Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/next-server/server/require.js:1:1397)
at loadComponents (/Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/next-server/server/load-components.js:1:1289)
at DevServer.findPageComponents (/Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/next-server/server/next-server.js:77:296)
at DevServer.renderErrorToHTML (/Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/next-server/server/next-server.js:139:209) {
code: 'MODULE_NOT_FOUND',
requireStack: [
'/Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/next-server/server/require.js',
'/Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/next-server/server/load-components.js',
'/Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/next-server/server/api-utils.js',
'/Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/next-server/server/next-server.js',
'/Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/server/next.js',
'/Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/server/lib/start-server.js',
'/Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/cli/next-dev.js',
'/Users/scottagirs/workspace/iJS/ijs.to/iJStoFE/node_modules/next/dist/bin/next'
]
}
To Reproduce
Upgrade to Next 11 and add next.config.js
module.exports = {
webpack(config, options) {
config.module.rules.push({
test: /\.svg$/,
use: ['@svgr/webpack'],
});
return config;
},
};
in package.json
"@svgr/webpack": "^5.5.0",
"next": "^11.0.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 116
- Comments: 38 (9 by maintainers)
Commits related to this issue
- Omit svg static imports if custom webpack config is defined (#26281) This PR does a couple things: 1. Omit svg static imports if the user has defined custom webpack config with svg rule 2. Change TS... — committed to vercel/next.js by styfle 3 years ago
- Omit svg static imports if custom webpack config is defined (#26281) This PR does a couple things: 1. Omit svg static imports if the user has defined custom webpack config with svg rule 2. Change TS... — committed to blitz-js/next.js by styfle 3 years ago
The fix is available in
next@11.0.1-canary.4You can try it out today with
yarn add next@canary. Thanks!I’m not entirely sure if #26281 and #26548 solve anything.
You test specifically for
rule.test.test('.svg')https://github.com/vercel/next.js/blob/8cbaa409ca7dce8786cef5625700df094c9c7cd9/packages/next/build/webpack-config.ts#L1563 but most people running SVGR and next.js have more complicated regexes than just/.svg$/. For them, this fix doesn’t do much.On top of that, you can now either use SVG in SVGR or in next/image, not both. At least not by default.
Consider our case. We filter all SVGs that match
/\.react\.svg/pattern (discord.react.svgwould match, whilecheckbox.svgwould not). Clearly, my regex pattern would not go through the current fix’s checks so the build fails onunsupported file type: undefinedbecause I cannot mix SVG in both next.js and SVGR.The workaround is to use this ugly config that includes the regex pattern for SVGR while excluding it in the next-image-loader (commented out part doesn’t work for some reason):
“Just use this config then” - Well, it’s incredibly ugly, requires maintenance from the user and if next.js is already trying to be smart, I don’t see why should I. Considering the logic is already there, can’t you just test if the non-next-image-loader RegExp contains .svg pattern (eg.
.svginreact.svg) and exclude the whole previous regex pattern in next-image-loader (as what I tried to do with exclude)?Because right now if you search for SVGR and next.js compatibility, you’ll find that it has been fixed. You test, realize the build is still not working and spend 4 hours tracking down the issues only to start reading the source code anyway. In the end, you realize the official support is minor and works on this specific issue that does not cover your use-case at all.
I don’t see a problem with the fix itself, but it would be nice if it would either support most (if not all) use-cases. It’s almost there already.
I have the same issue. When I add
webpack5: falsein next.config.js it works. I am using next-images without any loader configuration.Thanks for reporting this issue!
A large percentage of Next.js user are relying on
@svgr/webpackand similar alternatives so we’ll get this fixed!See PR #26281
For anyone still looking for a solution, to add to what @dohomi suggested in https://github.com/vercel/next.js/issues/26130#issuecomment-863299674 - in case u’re using SVGO or need to configure any other options, this should work just fine:
next.config.jsI thought a lot about this change. First I was frustrated, then I reflected on how I am using SVGs. I’ve also been utilising
SVGRto inline my SVGs. But only to be able to re-color and modify a few SVG attributes.It actually makes sense to me not to inline SVGs if these are my requirements. But so far I had not thought about a different approach. Asking Google, it turned out it’d be an option to leverage the
<use/>tag in combination with thesrcattribute that comes withStaticImageData. However, this requires all SVGs to have a uniqueidas in the following snippet:That was not the case for my assets.
Therefore I experimented with a few webpack loaders. I tried
svg-as-symbol-loaderandstring-replace-loader. The former had to be modified, the latter only allowed a simple string replacement, which can easily fail or be insufficient.However, the experiment turned out to solve my issue AND my SVGs can now be cached properly while still being able to use my SVG attributes as before. I also don’t have to use an
imgtag, which I see as a less flexible alternative as we cannot style thesvgany longer.I went a step further and created a custom loader that is a bit smarter than those I tried before. You can find it here together with some docs that further describe its usage and implementation:
https://www.npmjs.com/package/svgid-loader
All it does is to add an
idattribute to the firstsvgelement it finds. Take a look at the sources here and leave your feedback.Might not be a solution for everyone, but I actually will stick with it from now on as it also reduces markup, enables caching and complies with how nextjs loads such assets.
I flagged this issue yesterday in Discord (see https://discordapp.com/channels/752553802359505017/752647196419031042/854484227047555102), the proposed solution by @styfle was to use
disableStaticImages: true, but this means losing support for some of the amazing new features fornext/images. The other proposed solution was to import SVGs asfs.readFile('./file.svg')and then setting them usingdangerouslySetInnerHTML, but I don’t think this is a viable solution as firstly the readFile needs to happen serverside and secondly it adds unnecessary complexity.I believe that inline SVGs are a very common use case and this should be patched in a minor version release soon. One option could be to exclude SVGs from static imports, when svgr/webpack is set up in next.config.js
I added a feature request to internalize SVGR into Next.js’s core webpack configuration. Please voice your support there!
https://github.com/vercel/next.js/issues/26157
I would argue, that an inline SVG almost always has benefits for your performance and therefore the lighthouse score over additional requests by loading them from an external file. If you get to the point that the number of child elements of your DOM becomes an issue you have other problems to look at first 😉.
Exceptions for this rule are complex SVGs, which should probably be imported as an image or even be stored in a more efficient format (such as webp / png).
I found a temporary workaround until an official solution is available.
Firstly, add the following to
next.config.js:After that, fix type checking errors via
patch-package:node_modules/next/types/global.d.ts:node_modules/next/dist/client/image.d.ts:Run
npx patch-package nextand make sure to apply the patch in every environment by installingpatch-packageand adding it as apostinstallscript.You can try this solution which does not disable static images: https://github.com/vercel/next.js/issues/25950#issuecomment-863298702
When I tested it the order mattered. Using
config.module.rules.pushin my example first applies thesvgo-loader, then thesvgid-loaderand afterwards thenext-image-loaderthat is set for all image assets by nextjs. So yes, it works well. I’m stripping styles and convert them to inline. Otherwise some styles seem to not be applied. And using theextendDefaultPluginsshould cover what SVGR comes out of the box with.I’m facing this issue too sadly. I make heavy use of both next/image and inline SVGs (using SVGR) in most of my Next projects so the workarounds propsed aren’t adequate. Curious why SVG was added to static image type definitions when the use cases for inlining SVGs are so common?
Was using
svg-url-loader, got following errorTried after installing
@svgr/webpackStill facing the same issue.