Storybook: Load svg using vue-svg-loader not working

Created on 9 Jul 2019  路  16Comments  路  Source: storybookjs/storybook

.storybook/webpack.config.js

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

// Export a function. Accept the base config as the only param.
module.exports = async ({ config, mode }) => {
  // `mode` has a value of 'DEVELOPMENT' or 'PRODUCTION'
  // You can change the configuration based on that.
  // 'PRODUCTION' is used when building the static version of storybook.
  // Make whatever fine-grained changes you need
  config.module.rules.push({
    test: /\.scss$/,
    use: [
      "style-loader", // creates style nodes from JS strings
      "css-loader", // translates CSS into CommonJS
      "sass-loader" // compiles Sass to CSS, using Node Sass by default
    ]
  });
  config.module.rules.push({
    test: /\.(png|jpg|gif)$/,
    use: [
      {
        loader: 'file-loader',
        options: {name: 'assets/[name].[hash:8].[ext]'},
      },
    ],
  });
  config.module.rules.push({
    test: /\.vue$/,
    use: [
      {
        loader: "vue-svg-inline-loader",
        options: { /* ... */ }
      }
    ]
  });
  config.module.rules.push({
    test: /\.svg$/,
    loader: 'vue-svg-loader',
    options: {name: 'assets/[name].[hash:8].[ext]'},
  });
  config.resolve.alias['@'] = path.resolve('src')

  // Return the altered config
  return { ...config, node: { fs: 'empty' } };
};

index.vue

<template>
  <div>
     <IconTest/>
  </div>
</template>
<script>
   import IconTest from "@/assets/svg/test-icon.svg"
   export default {
      components:{
         IconTest
      }
   }
</script>

error:

vue compatibility with other tools has workaround inactive question / support

Most helpful comment

@devtoni's workaround worked for me too, with one additional change.

As of Storybook v6.9.0, I had to add pdf to the test regex that was being replaced. The updated code that worked for me is:

  config.module.rules = config.module.rules.map(rule => {
    if (String(rule.test) === String(/\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/)) {
      return {
        ...rule,
        test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/,
      };
    }
    return rule;
  });

All 16 comments

Automention: Hey @backbone87 @elevatebart @pksunkara, you've been tagged! Can you give a hand here?

@gsaada Run storybook with --debug-webpack webpack to see the entire webpack config. It's almost certainly the case that the rules you're adding are conflicting with existing rules added by storybook. The solution is probably to remove those conflicting rules.

@shilman Thank you for your response.
Here's the output after I add this flag.
As you can see there are 2 configs. I don't see any conflict.

{ name: 'manager',
  mode: 'development',
  bail: false,
  devtool: 'none',
  entry: [ '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@storybook/core/dist/server/common/polyfills.js',
     '/mnt/c/Users/saada/Development/novos-client-ow-app/app/.storybook/addons.js',
     '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@storybook/core/dist/client/manager/index.js'
  ],
  output: { path:
      '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@storybook/core/dist/public',
     filename: '[name
    ].[chunkhash
    ].bundle.js',
     publicPath: ''
  },
  cache: {},
  plugins: [ DllReferencePlugin {
       options: { context:
           '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/node_modules',
          manifest:
           '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@storybook/core/dll/storybook_ui-manifest.json'
      }
    },
     HtmlWebpackPlugin {
       options: { template:
           '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@storybook/core/dist/server/templates/index.ejs',
          templateContent: false,
          templateParameters: [Function: templateParameters
        ],
          filename: 'index.html',
          hash: false,
          inject: false,
          compile: true,
          favicon: false,
          minify: undefined,
          cache: true,
          showErrors: true,
          chunks: 'all',
          excludeChunks: [],
          chunksSortMode: 'none',
          meta: {},
          title: 'Webpack App',
          xhtml: false,
          alwaysWriteToDisk: true
      }, 
       childCompilerHash: undefined,
       childCompilationOutputName: undefined,
       assetJson: undefined,
       hash: undefined,
       version: 4
    },
     DefinePlugin {
       definitions: { 'process.env': { NODE_ENV: '"development"',
             NODE_PATH: '""',
             PUBLIC_URL: '""',
             VUE_APP_SERVER_URL: '"http://localhost:4000"',
             VUE_APP_GOOGLE_ANALYTICS_ID: '"UA-110665184-4"'
        }
      }
    },
     CaseSensitivePathsPlugin { options: {}, pathCache: {}, fsOperations: 0, primed: false
    },
     DefinePlugin {
       definitions: { 'process.env.VUE_APP_SERVER_URL': '"http://localhost:4000"',
          'process.env.VUE_APP_GOOGLE_ANALYTICS_ID': '"UA-110665184-4"'
      }
    }
  ],
  module: { rules: [
      { test: /\.(mjs|jsx?)$/,
          use: [
          { loader: 'babel-loader',
               options: { cacheDirectory:
                   '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/.cache/storybook',
                  presets: [
                [ '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@babel/preset-env/lib/index.js',
                  { shippedProposals: true, useBuiltIns: 'usage'
                  }
                ]
              ], 
                  plugins: [ '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@babel/plugin-proposal-object-rest-spread/lib/index.js',
                     '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@babel/plugin-proposal-class-properties/lib/index.js',
                     '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@babel/plugin-syntax-dynamic-import/lib/index.js',
                [ '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.js',
                  { sourceMap: true, autoLabel: true
                  }
                ],
                     '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/babel-plugin-macros/dist/index.js'
              ]
            }
          }
        ],
          include: [ '/mnt/c/Users/saada/Development/novos-client-ow-app/app'
        ],
          exclude: [ '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules'
        ]
      }
    ]
  },
  resolve: { extensions: [ '.mjs', '.js', '.jsx', '.json'
    ],
     modules: [ 'node_modules'
    ],
     alias: { '@emotion/core':
         '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@emotion/core',
        '@emotion/styled':
         '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@emotion/styled',
        '@storybook/addons':
         '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@storybook/addons',
        '@storybook/channels':
         '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@storybook/channels',
        '@storybook/components': 
         '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@storybook/components',
        '@storybook/core-events':
         '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@storybook/core-events',
        '@storybook/router':
         '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@storybook/router',
        '@storybook/theming':
         '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@storybook/theming',
        '@storybook/ui':
         '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@storybook/ui',
        'core-js':
         '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/core-js',
        'emotion-theming':
         '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/emotion-theming',
        'prop-types': 
         '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/prop-types',
        react:
         '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/react',
        'react-dom':
         '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/react-dom'
    }
  },
  recordsPath:
   '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/.cache/storybook/records.json',
  optimization: { splitChunks: { chunks: 'all'
    }, runtimeChunk: true
  }
}

**info => Loading custom webpack config (full-control mode). 
Preview webpack config**

{ mode: 'development',
  bail: false,
  devtool: '#cheap-module-source-map',
  entry: [ '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@storybook/core/dist/server/common/polyfills.js',
     '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@storybook/core/dist/server/preview/globals.js',
     '/mnt/c/Users/saada/Development/novos-client-ow-app/app/.storybook/config.js',
     '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/webpack-hot-middleware/client.js?reload=true'
  ],
  output: { path:
      '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@storybook/core/dist/public',
     filename: '[name
    ].[hash
    ].bundle.js',
     publicPath: ''
  },
  plugins: [ HtmlWebpackPlugin {
       options: { template:
           '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@storybook/core/dist/server/templates/index.ejs',
          templateContent: false,
          templateParameters: [Function: templateParameters
        ],
          filename: 'iframe.html',
          hash: false,
          inject: false,
          compile: true,
          favicon: false,
          minify: undefined,
          cache: true,
          showErrors: true,
          chunks: 'all',
          excludeChunks: [],
          chunksSortMode: 'none',
          meta: {},
          title: 'Webpack App',
          xhtml: false,
          alwaysWriteToDisk: true
      },
       childCompilerHash: undefined,
       childCompilationOutputName: undefined,
       assetJson: undefined,
       hash: undefined,
       version: 4
    },
     DefinePlugin {
       definitions: { 'process.env': { NODE_ENV: '"development"',
             NODE_PATH: '""',
             PUBLIC_URL: '"."',
             VUE_APP_SERVER_URL: '"http://localhost:4000"',
             VUE_APP_GOOGLE_ANALYTICS_ID: '"UA-110665184-4"'
        }
      }
    }, 
     WatchMissingNodeModulesPlugin {
       nodeModulesPath:
        '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules'
    },
     HotModuleReplacementPlugin {
       options: {},
       multiStep: undefined,
       fullBuildTimeout: 200,
       requestTimeout: 10000
    },
     CaseSensitivePathsPlugin { options: {}, pathCache: {}, fsOperations: 0, primed: false
    },
     ProgressPlugin {
       profile: false,
       handler: undefined,
       modulesCount: 500,
       showEntries: false,
       showModules: true,
       showActiveModules: true
    },
     DefinePlugin { 
       definitions: { 'process.env.VUE_APP_SERVER_URL': '"http://localhost:4000"',
          'process.env.VUE_APP_GOOGLE_ANALYTICS_ID': '"UA-110665184-4"'
      }
    },
     VueLoaderPlugin {}
  ],
  module: { rules: [
      { test: /\.(mjs|jsx?)$/,
          use: [
          { loader: 'babel-loader',
               options: { cacheDirectory:
                   '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/.cache/storybook',
                  presets: [
                [ '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@babel/preset-env/lib/index.js',
                  { shippedProposals: true, useBuiltIns: 'usage'
                  }
                ],
                     '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/babel-preset-vue/dist/babel-preset-vue.cjs.js'
              ],
                  plugins: [ '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@babel/plugin-proposal-object-rest-spread/lib/index.js',
                     '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@babel/plugin-proposal-class-properties/lib/index.js',
                     '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/@babel/plugin-syntax-dynamic-import/lib/index.js',
                [ '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/babel-plugin-emotion/dist/babel-plugin-emotion.cjs.js',
                  { sourceMap: true, autoLabel: true
                  }
                ],
                     '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/babel-plugin-macros/dist/index.js'
              ]
            }
          }
        ],
          include: [ '/mnt/c/Users/saada/Development/novos-client-ow-app/app'
        ],
          exclude: [ '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules'
        ]
      },
      { test: /\.md$/,
          use: [
          { loader:
                '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/raw-loader/index.js'
          }
        ]
      },
      { test: /\.vue$/,
          loader:
           '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/vue-loader/lib/index.js',
          options: {}
      },
      { test: /\.css$/,
          use: [ '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/style-loader/index.js',
          { loader:
                '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/css-loader/dist/cjs.js',      
               options: { importLoaders: 1
            }
          },
          { loader:
                '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/postcss-loader/src/index.js', 
               options: { ident: 'postcss', postcss: {}, plugins: [Function: plugins
              ]
            }
          }
        ]
      },
      { test:
           /\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani)(\?.*)?$/,
          loader:
           '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/file-loader/dist/cjs.js',
          query: { name: 'static/media/[name
          ].[hash: 8
          ].[ext
          ]'
        }
      },
      { test: /\.(mp4|webm|wav|mp3|m4a|aac|oga)(\?.*)?$/,
          loader:
           '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/url-loader/dist/cjs.js',
          query: { limit: 10000, name: 'static/media/[name
          ].[hash: 8
          ].[ext
          ]'
        }
      },
      { test: /\.scss$/,
          use: [ 'style-loader', 'css-loader', 'sass-loader'
        ]
      },
      { test: /\.(png|jpg|gif)$/,
          use: [
          { loader: 'file-loader',
               options: { name: 'assets/[name
              ].[hash: 8
              ].[ext
              ]'
            }
          }
        ]
      },
      { test: /\.vue$/,
          use: [
          { loader: 'vue-svg-inline-loader', options: {}
          }
        ]
      },
      { test: /\.svg$/,
          loader: 'vue-svg-loader',
          options: { name: 'assets/[name
          ].[hash: 8
          ].[ext
          ]'
        }
      }
    ]
  },
  resolve: { extensions: [ '.mjs', '.js', '.jsx', '.json', '.vue'
    ],
     modules: [ 'node_modules'
    ],
     alias: { 'core-js':
         '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/core-js',
        react:
         '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/react',
        'react-dom':
         '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/react-dom',
        'vue$':
         '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/vue/dist/vue.esm.js',
        '@': '/mnt/c/Users/saada/Development/novos-client-ow-app/app/src'
    }
  },
  optimization: { splitChunks: { chunks: 'all'
    },
     runtimeChunk: true,
     minimizer: [ TerserPlugin {
          options: { test: /\.m?js(\?.*)?$/i,
             chunkFilter: [Function: chunkFilter
          ],
             warningsFilter: [Function: warningsFilter
          ],
             extractComments: false,
             sourceMap: true,
             cache: true,
             cacheKeys: [Function: cacheKeys
          ], 
             parallel: true,
             include: undefined,
             exclude: undefined,
             minify: undefined,
             terserOptions: { output: { comments: /^\**!|@preserve|@license|@cc_on/i
            },
                mangle: false,
                keep_fnames: true
          }
        }
      }
    ]
  },
  performance: { hints: false
  },
  node: { fs: 'empty'
  }
}

@gsaada I see a conflict:

      { test:
           /\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani)(\?.*)?$/,
          loader:
           '/mnt/c/Users/saada/Development/novos-client-ow-app/app/node_modules/file-loader/dist/cjs.js',
          query: { name: 'static/media/[name
          ].[hash: 8
          ].[ext
          ]'
        }
      },

I had a similar problem, the reason as shilman commented is because storybook by default in its webpack configuration is using file-loader for the svgs. So it founds a conflict with the vue-svg-loader.

What I did in my case and it works is to remove from the regex the svg extension and then as you did push the new rule of svg with the vue-svg-loader.

config.module.rules = config.module.rules.map(rule => {
        {
            if (String(rule.test) === String(/\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani)(\?.*)?$/)) {
                return {
                    ...rule,
                    test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani)(\?.*)?$/,
                };
            }

            return rule;
        }
    });

Thank you @devtoni !
Works!

Thanks @devtoni !
I was running into this trying to use svg-inline-loader.

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

Hey there, it's me again! I am going close this issue to help our maintainers focus on the current development roadmap instead. If the issue mentioned is still a concern, please open a new ticket and mention this old one. Cheers and thanks for using Storybook!

@devtoni's workaround worked for me too, with one additional change.

As of Storybook v6.9.0, I had to add pdf to the test regex that was being replaced. The updated code that worked for me is:

  config.module.rules = config.module.rules.map(rule => {
    if (String(rule.test) === String(/\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/)) {
      return {
        ...rule,
        test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/,
      };
    }
    return rule;
  });

I'm having issues with this! Look at my webpack.config.js

const path = require("path");

module.exports = ({ config }) => {
    config.module.rules = config.module.rules.map(rule => {
        if (
            String(rule.test) === String(/\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/)
        ) {
            return {
                ...rule,
                test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/
            };
        }
        return rule;
    });

    config.module.rules.push({
        test: /\.scss$/,
        use: [
            "vue-style-loader",
            "css-loader",
            {
                loader: "sass-loader",
                options: {
                    data: `
                        @import '~bulma/sass/utilities/initial-variables';
                        @import "@/assets/sass/partials/_variables.scss";
                        @import '~bulma/sass/utilities/functions';
                        @import '~bulma/sass/utilities/derived-variables';
                        @import '~bulma/sass/utilities/mixins';
                    `
                }
            }
        ]
    });
    config.module.rules.push({
        test: /\.(svg)(\?.*)?$/,
        oneOf: [
            {
                resourceQuery: /inline/,
                use: [
                    {
                        loader: "vue-svg-loader",
                        options: {
                            svgo: {
                                plugins: [
                                    { cleanupIDs: false },
                                    { removeComments: true },
                                    { removeDimensions: true },
                                    { removeViewBox: false }
                                ]
                            }
                        }
                    }
                ]
            },
            {
                loader: "file-loader",
                options: {
                    name: "assets/imgs/[name].[hash:8].[ext]"
                }
            }
        ],
        use: [
            {
                loader: "babel-loader"
            }
        ]
    });

    config.resolve.alias = {
        ...config.resolve.alias,
        "@": path.resolve(__dirname, "../src")
    };

    return config;
};

and when I serve storybook it doesn't print any error in console
image

but when I try to open in on browser:
image

Solved my issue with this configuration:

config.module.rules.push({
        test: /\.(svg)(\?.*)?$/,
        use: "babel-loader",
        oneOf: [
            {
                resourceQuery: /inline/,
                use: [
                    {
                        loader: "vue-svg-loader",
                        options: {
                            svgo: {
                                plugins: [
                                    { removeDoctype: true },
                                    { removeComments: true },
                                    { removeDimensions: true },
                                    { cleanupIDs: false },
                                    { removeViewBox: false }
                                ]
                            }
                        }
                    }
                ]
            },
            {
                loader: "file-loader",
                options: {
                    name: "assets/imgs/[name].[hash:8].[ext]"
                }
            }
        ],
        exclude: /(node_modules)/
    });

Thanks @danthareja that worked

https://github.com/storybookjs/vue-cli-plugin-storybook/issues/69#issuecomment-668171815

This config solved the issue for me:

module.exports = ({ config }) => {

    let rule = config.module.rules.find(r =>
        // it can be another rule with file loader 
        // we should get only svg related
        r.test && r.test.toString().includes('svg') &&
        // file-loader might be resolved to js file path so "endsWith" is not reliable enough
        r.loader && r.loader.includes('file-loader')
    );
    rule.test = /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani)(\?.*)?$/;

    config.module.rules.push(
        {
            test: /\.svg$/,
            use: ['vue-svg-loader']
        }
    )

    // ...

    return config;
}

Original post: https://stackoverflow.com/questions/56971513/storybook-does-not-load-svgs-in-components-of-vue-project

Thanks @ecarrera that worked for me as well. Been struggling with this all day!

@ndelangen Does other frameworks have this same issue of svg not working?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Gongreg picture Gongreg  路  58Comments

43081j picture 43081j  路  61Comments

dependencies[bot] picture dependencies[bot]  路  142Comments

ilias-t picture ilias-t  路  73Comments

maraisr picture maraisr  路  119Comments