willem
willem

Reputation: 27027

In NextJS, how to build when referencing external npm package that contains SVGs?

My nextjs project (let's call it TheHost) references another npm package (let's call it ThePackage).

SVGs load fine when defined within TheHost, but importing ThePackage fails because next tries to interpret the svg as javascript... so I get the following error when doing a next build:

SyntaxError: Unexpected token '<'

To re-iterate, SVGs work fine when referencing an svg defined in TheHost itself. The problem seems to be importing npm packages that contain SVGs.

It doesn't matter if I import a component from ThePackage that uses an SVG or not, just the fact that somewhere in the npm package it contains an "import xxx from '../path/to/svg' is enough to break next build.

For what it's worth, the ThePackage's transpiled javascript reads the svg as follows:

var _mysvg = require("../path/to/the-svg.svg");

Lots of details:

The next build stack trace is:

> Using external babel configuration
> Location: "/Users/w/dev/TheHost/.babelrc"
Creating an optimized production build

Compiled successfully.

> Build error occurred
/Users/w/dev/TheHost/node_modules/ThePackage/build/assets/card_background.svg:1
<svg viewBox="0 0 860 382" fill="none" xmlns="http://www.w3.org/2000/svg">
^

SyntaxError: Unexpected token '<'
    at Module._compile (internal/modules/cjs/loader.js:895:18)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:995:10)
    at Module.load (internal/modules/cjs/loader.js:815:32)
    at Function.Module._load (internal/modules/cjs/loader.js:727:14)
    at Module.require (internal/modules/cjs/loader.js:852:19)
    at require (internal/modules/cjs/helpers.js:74:18)
    at Object.<anonymous> (/Users/w/dev/TheHost/node_modules/TheProject/build/card/style.js:14:47)
    at Module._compile (internal/modules/cjs/loader.js:959:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:995:10)
    at Module.load (internal/modules/cjs/loader.js:815:32) {
  type: 'SyntaxError'
}
Automatically optimizing pages .%

The .babelrc file:

{
  "presets": [
    "next/babel"
  ],
  "plugins": [
    "babel-plugin-styled-components",
    "inline-react-svg"
  ]
}

The next.config.js file:

const withSourceMaps = require("@zeit/next-source-maps");
const withImages = require("next-images");

module.exports = withImages(
  withSourceMaps({
    env: { *** redacted *** },
    publicRuntimeConfig: { *** redacted *** },
    webpack: (config, options) => {
      if (!options.isServer) {
        config.resolve.alias["@sentry/node"] = "@sentry/browser";
      }
      config.module.rules.push({
        test: /\.svg$/,
        use: ["@svgr/webpack"]
      });
      return config;
    }
  })
);

The nextjs svgr package versions are as follows:

"next": "^9.2.1",
"next-images": "^1.3.0",
"@svgr/webpack": "^5.1.0",
"babel-eslint": "^10.0.3",
"babel-plugin-inline-react-svg": "^1.1.1",

ThePackage is built with the following output configuration (webpack):

  entry: './src/index.js',
  output: {
    path: buildFolder,
    filename: 'ThePackage.js',
    library: 'ThePackage',
    libraryTarget: 'umd', /* Note: umd */
    umdNamedDefine: true
  },

Upvotes: 10

Views: 9425

Answers (2)

yem
yem

Reputation: 755

As stated in Seagyn's answer, NextJS ignores node_modules by default and as a result @svgr/webpack is not being run on the SVGs that are in the node_modules, leading to your error.

As of NextJS Version 13 a transpilePackages option was added to the next.config.js which allows you to pass an array of packages that you want to be transpiled in addition to your source code.

All you have to do now is add the following to your next.config.js:

/** @type {import('next').NextConfig} */
const nextConfig = {
  transpilePackages: ['ThePackage'],  //  <- Add this
}
 
module.exports = nextConfig

Add the package which includes the SVGs to that array and that should solve your issue.

You can see the docs here.

Upvotes: 1

Seagyn Davis
Seagyn Davis

Reputation: 146

NextJS ignores node_modules by default so you'd need to specifically allow your config to be able to transpile your package. Fortunately, someone has already created a NextJS plugin to allow this: https://github.com/martpie/next-transpile-modules

I'd also suggest using Next Compose Plugins to neaten up the config. In the end, your next.config.js will look something like this:

const withSourceMaps = require("@zeit/next-source-maps");
const withImages = require("next-images");
const withPlugins = require('next-compose-plugins');
const withTM = require('next-transpile-modules')(['ThePackage']);

module.exports = withPlugins([
    withTM,
    [
        withImages,
        {
            exclude: /\.svg$/
        }
    ],
    withSourceMaps
],
{
    env: { *** redacted *** },
    publicRuntimeConfig: { *** redacted *** },
    webpack: (config, options) => {
      if (!options.isServer) {
        config.resolve.alias["@sentry/node"] = "@sentry/browser";
      }
      config.module.rules.push({
        test: /\.svg$/,
        use: ["@svgr/webpack"]
      });
      return config;
    }
});

I've also excluded SVGs from being processed by withImages.

Upvotes: 13

Related Questions