4

Can't use SVG as modules for both NextJS app and Storybook, imported with absolute paths. With many tried setups I was able to import SVG either in Next or in Storybook, but not both. I used this setup for babel-plugin-inline-react-svg:

// .babelrc
{
  ...
  "plugins": [
    ...
    "inline-react-svg"
  ]
}

With this plugin Storybook doesn't require any configuration and this example code works as expected:

import Wrapper from '../../../components/Wrapper';
import IconSVG from '../../../icons/sandwich.svg';

export const WrappedSVG = () => (
  <Wrapper>
    <IconSVG />
  </Wrapper>
);

But the following does not:

import Wrapper from 'components/Wrapper';
import IconSVG from 'icons/sandwich.svg';

export const WrappedSVG = () => (
  <Wrapper>
    <IconSVG />
  </Wrapper>
);

Wrapper is being processed, but not the icon: Cannot find module

Here is svgr setup:

// next.config.js
module.exports = {
  webpack(config, options) {
    ...
    config.module.rules.push({
      test: /\.svg$/,
      use: ['@svgr/webpack'],
    });

    return config;
  },
};
// .storybook/main.js
module.exports = {
  ...
  webpackFinal: async config => {
    ...
    config.module.rules.unshift({
      test: /\.svg$/,
      use: ['@svgr/webpack'],
    });

    return config;
  },
}

This configuration works fine on the app side, but in Storybook I get DOMException: "String contains an invalid character"

My npm run dev script is this: ts-node --project tsconfig.server.json src/server.ts (via nodemon).

I hope somebody would give me a hint how to make absolute import of SVG components work for both NextJS and Storybook.

p.boiko
  • 1,026
  • 9
  • 10

4 Answers4

6

There's an answer to a different problem that worked for me here.

It might be because a conflict with Storybook's default SVG loader. Here's what worked for me:

module.exports = {
  webpackFinal: async (config, { configType }) => {
    const rules = config.module.rules;
    const fileLoaderRule = rules.find(rule => rule.test.test('.svg'));
    fileLoaderRule.exclude = /\.svg$/;

    rules.push({
      test: /\.svg$/,
      use: ["@svgr/webpack"],
    });

    return config;
  }
};
Brendan
  • 868
  • 1
  • 16
  • 38
2

Regarding absolute paths, Next.js now supports the aliases in file imports with minimal custom configuration in tsconfig.json/jsconfig.json files (No need for webpack configuration (*experimental stage)).

To configure, have a look here!

0

It can be done by configuration webpack. Create the next.config.js file, at the project root, it it doesn't exists, and add this:

//next.config.js

const path = require('path');
const webpack = require('webpack');

module.exports = {
  webpack: config => {
    config.resolve.alias['~'] = path.resolve(__dirname);
    return config;
  }
};

How to use:

import myimage from '~/somepath/myimage.svg'

More details at this nice tutorial.

Check Weppack documentation here.

For multiple plugin configuration at next.config.js check this.

MiguelSlv
  • 14,067
  • 15
  • 102
  • 169
0

This is what I did to make it work with a similar setup.

You want to install and add the babel-plugin-module-resolver into .babelrc like this where root is the root of the absolute path you want to start from.

{
  "presets": [
    "next/babel"
  ],
  "plugins": [
    ["module-resolver", {
      "root": ["."]
    }],
    ...
  ]
}

The problem is Babel runs before Webpack so even though you setup the rules it won't work because svgs are processed by Babel.

Then I installed this tsconfig-paths-webpack-plugin which bring the baseUrl and paths from tsconfig.json.

module.exports = {
  webpack: config => {
    config.resolve.plugins = [new TsconfigPathsPlugin()]
    
    return config;
  }
};

Now you should be able to import svgs like this:

// components/myComponent/myDeepComponent.tsx
import Icon from "assets/icons/icon.svg"

The trick is you have to understand is that Babel runs long before Webpack. You can read more in this github thread which made me realise the issue.