Nx: Cannot load SVG in React application

Created on 9 Oct 2019  ·  34Comments  ·  Source: nrwl/nx

  • [x] I am running the latest version
  • [x] I checked the documentation and found no answer
  • [x] I checked to make sure that this issue has not already been filed
  • [x] I'm reporting the issue to the correct repository (not related to Angular, AngularCLI or any dependency)

Expected Behavior

Utilize an SVG file using the <svg> tag in a React application.

Current Behavior

The application fails to compile with nx serve.

Failure Information (for bugs)

Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.

Steps to Reproduce

I personally discovered this when trying to use the Ionic React components in an Nx workspace, so will provide these steps to reproduce.

  1. Generate a new Nx workspace with a default React application
  2. yarn install @ionic/react
  3. Wrap the contents of the default app.tsx JSX with <IonApp></IonApp>
  4. nx serve

If you can provide steps to reproduce from scratch, that would be enormously appreciated (i.e. where the first step is npx create-nx-workspace@latest repro-workspace)

Context

Please provide any relevant information about your setup:

  • Nx 8.6.0
  • @ionic/react 4.10.0-rc.3

Failure Logs

ERROR in /Users/devinshoemaker/code/ionic/test-nx-ionic/node_modules/ionicons/dist/ionicons/svg/md-quote.svg 1:0
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M96.4 416h77.1l50.9-96.6V96h-160v223.4h77.1L96.4 416zm224 0h77.1l50-96.6V96H288.4v223.4h82l-50 96.6z"/></svg>

ERROR in /Users/devinshoemaker/code/ionic/test-nx-ionic/node_modules/ionicons/dist/ionicons/svg/ios-school.svg 1:0
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M96.9 270.3V363c0 2.9 1.5 5.5 4 7l132 75.9c5.3 3.1 12-.8 12-7v-93.8c0-2.9-1.5-5.5-4-7l-132-74.9c-5.4-2.9-12 1-12 7.1zM280.9 445.9L413 370c2.5-1.4 4-4.1 4-7v-93.7c0-6.2-6.6-10-12-7l-132 75.9c-2.5 1.4-4 4.1-4 7V439c-.1 6.1 6.6 10 11.9 6.9z"/><path d="M249 65.1L37 188.9c-5.4 3.1-5.4 10.8 0 13.9l212 117.8c4.9 2.8 11 2.8 15.9 0L453 212.9c5.3-3.1 7 .8 7 7v153.4c0 6.8 3.9 10 11 10 4.4 0 10-3.2 10-10V201.5c0-2.9-1.5-5.5-4-7L264.9 65.1c-4.9-2.8-11-2.8-15.9 0z"/></svg>

ERROR in /Users/devinshoemaker/code/ionic/test-nx-ionic/node_modules/ionicons/dist/ionicons/svg/md-arrow-round-down.svg 1:0
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M99.4 284.9l134 138.1c5.8 6 13.7 9 22.4 9h.4c8.7 0 16.6-3 22.4-9l134-138.1c12.5-12 12.5-31.3 0-43.2-12.5-11.9-32.7-11.9-45.2 0l-79.4 83v-214c0-16.9-14.3-30.6-32-30.6-18 0-32 13.7-32 30.6v214l-79.4-83c-12.5-11.9-32.7-11.9-45.2 0s-12.5 31.2 0 43.2z"/></svg>

Other

I have found one other person that reported a similar issue, though it's not Ionic specific. The solution proposed here involved overriding the default webpack config that Nx uses for it's React applications, however that config did not work for me, and I am not adept enough with webpack yet to get this working. I have been inspecting the create-creact-app webpack.config.js (which is what Ionic uses in it's generated projects) and will continue to try and get an override working, but this does seem like something that should be available out of the box. create-react-app applications are quite popular, so I imagine many people will eventually run into this issue when using React with Nx.

https://stackoverflow.com/questions/56848952/cannot-load-svg-in-nrwl-react-workspace

react bug

Most helpful comment

No worries, we'll make sure testing works well.

All 34 comments

I struggled with this yesterday and resorted to just using the file's path as the src attribute

Thanks for the response, it's nice to know that I'm not alone. I'm glad that worked for you, but that's a bummer for my situation as I cannot control what Ionic does.

Some of the solutions I found were adding a custom.d.ts file in the root of the project and including:

declare module '*.svg' {
    const content: string;
    export default content;
}

And/or using a custom webpack config with a loader for svg's etc.

There a few solutions floating around that seem to be working for others, but nothing worked for me. I'll be keeping an eye on this issue since I'd prefer to be able to import them directly.

@devinshoemaker
I had the same problem during integrating Nx and @ionic/react and I solved it by using a custom webpack config.

  • Install Ionic
$ npm i @ionic/react @ionic/react-router react-router-dom ionicons
$ npm i -D [email protected]
  • Install additional loaders
$ npm i -D @svgr/webpack url-loader
  • webpack.config.js
// @nrwl/react + SVG
const babelWebpackConfig = require('@nrwl/react/plugins/babel');

module.exports = config => {
  config.module.rules.push(
    {
      test: /\.svg$/,
      use: [
        '@svgr/webpack',
        'url-loader'
      ]
    }
  );
  return babelWebpackConfig(config);
};
  • workspace.json
{
  "version": 1,
  "projects": {
    "client": {
      "root": "apps/client",
      "sourceRoot": "apps/client/src",
      "projectType": "application",
      "schematics": {},
      "architect": {
        "build": {
          "builder": "@nrwl/web:build",
          "options": {
            ...
            "webpackConfig": "./webpack.config.js"
          },
  • polyfill.ts (not required in recent @ionic/react)
(window as any).process = {
  env: {}
};

@puku0x Thanks! This worked for me without the polyfill.ts (but i'm not using ionic, so maybe thats it)

I tried a couple of different webpack.config.js variations, but no worked. Thanks for yours, works like a charm!

@puku0x Hey thanks for the workaround. We can add SVG support in the web builder so any app using it will benefit -- including React.

What is the polyfill entry for? Do you get an error without it?

@jaysoo Thank you. I am looking forward to supporting SVG. Actually, the polyfill is needed only for Ionic React because it requires process.env to detect development mode.

image

@puku0x Hmm, the build already replaces process.env.NODE_ENV with production or development, but looks like ionic is looking for process.env which we don't substitute. This can be fixed as well. :)

In addition, as mentioned in Jest Docs, a file mock is needed to run tests.

// jest.config.js
module.exports = {
  testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'],
  transform: {
    '^.+\\.(ts|js|html)$': 'ts-jest',
  },
  resolver: '@nrwl/jest/plugins/resolver',
  moduleFileExtensions: ['ts', 'js', 'html'],
  coverageReporters: ['html'],
  passWithNoTests: true,
  moduleNameMapper: {
    '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
      '<rootDir>/src/app/__mocks__/fileMock.js',
  },
};
// apps/client/src/app/__mocks__/fileMock.js
module.exports = 'test-file-stub';

No worries, we'll make sure testing works well.

@jaysoo Thank you very much!

Thank you! I can’t wait to use Nx for my Ionic projects. 😁

I've opened a PR, but there is one thing I want to potentially tackle before it goes out.

+1

Just wanted to link the PR to this issue for easier tracking #1964

https://github.com/nrwl/nx/pull/1964 has been merged! Thank you @jaysoo !

Is anyone able to get v8.10.1 to work? It keeps saying it can't find the module.

@stramel Unfortunately, not yet.

I could load SVGs when I commented out issuer from @nrwl/react/plugins/webpack.js

image

Current work around for Nx v8.10 + Ionic/React.

  • webpack.config.js
const getWebpackConfig = require('@nrwl/react/plugins/webpack');

function getCustomWebpackConfig(webpackConfig) {
  const config = getWebpackConfig(webpackConfig);

  // SVG fix
  delete config.module.rules.find(rule => rule.test.test('.svg')).issuer;

  // // TS v3.7 support
  // const babelRuleOptions = config.module.rules.find(r => r.loader === 'babel-loader').options;
  // babelRuleOptions.plugins.push('@babel/plugin-proposal-optional-chaining');
  // babelRuleOptions.plugins.push('@babel/plugin-proposal-nullish-coalescing-operator');

  return config;
}

module.exports = getCustomWebpackConfig;
  • workspace.json
{
  "version": 1,
  "projects": {
    "client": {
      "root": "apps/client",
      "sourceRoot": "apps/client/src",
      "projectType": "application",
      "schematics": {},
      "architect": {
        "build": {
          "builder": "@nrwl/web:build",
          "options": {
            ...
            "webpackConfig": "./webpack.config.js"
          },
  • apps/client/jest.config.js
module.exports = {
  name: 'client',
  preset: '../../jest.config.js',
  transform: {
    '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',
    '^.+\\.[tj]sx?$': 'ts-jest',
  },
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
  coverageDirectory: '../../coverage/apps/client',
  moduleNameMapper: {
    '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
      '<rootDir>/src/app/__mocks__/fileMock.js',
  },
  modulePathIgnorePatterns: ['<rootDir>/.*/__mocks__'],
};
  • apps/client/src/app/__mocks__/fileMock.js
module.exports = 'test-file-stub';

Hmmm @puku0x I wasn't able to get it working using that webpack config...

@jaysoo Is there documentation around what needs updated or a way to update the apps with the required config? When I run nx serve nextjs-app it gives me this error message:

ModuleParseError: Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>icn</title><path d="M18,13H13v5a1,1,0,0,1-2,0V13H6a1,1,0,0,1,0-2h5V6a1,1,0,0,1,2,0v5h5a1,1,0,0,1,0,2Z"/></svg>
    at handleParseError (/project/node_modules/webpack/lib/NormalModule.js:469:19)
    at /project/node_modules/webpack/lib/NormalModule.js:503:5
    at /project/node_modules/webpack/lib/NormalModule.js:358:12
    at /project/node_modules/loader-runner/lib/LoaderRunner.js:373:3
    at iterateNormalLoaders (/project/node_modules/loader-runner/lib/LoaderRunner.js:214:10)
    at /project/node_modules/loader-runner/lib/LoaderRunner.js:205:4
    at /project/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:85:15
    at processTicksAndRejections (internal/process/task_queues.js:75:11)

@puku0x I figured out the issue with mine. They applied the change only to the react application with their build chain. Next.js needs these steps:

Add this to your project's tsconfig.json

+ "files": [
+    "../../node_modules/@nrwl/react/typings/image.d.ts"
+  ],

Add this to your project's jest.config.js in the transform object.

+ '^(?!.*\\\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest'

Add this to your project's next.config.js

+ webpack: config => {
+    // Converts SVG files into React components.
+    config.module.rules.push({
+      test: /\.svg$/,
+      issuer: {
+        test: /\.[jt]sx?$/
+      },
+      use: ['@svgr/webpack?-svgo,+titleProp,+ref![path]', 'url-loader']
+    });
+
+    return config
+  }

Install @svgr/webpack and url-loader to your devDependencies.

Everything is working for me now!

So is there a ready solution to the problem? how to add my own loader for svg files?

i want to use

        use: [
            {
                loader: 'svg-sprite-loader',
                options: {
                    symbolId: '[name]'
                }
            },
            'svgo-loader'
        ]

as loader.
but now i get base64 src string from svg imports

@Krasnopir Does this work for you?

const getWebpackConfig = require('@nrwl/react/plugins/webpack');

function getCustomWebpackConfig(webpackConfig) {
  const config = getWebpackConfig(webpackConfig);

  // SVG fix
  const index = config.module.rules.findIndex(rule => rule.test.test('.svg'));
  config.module.rules.splice(index, 1, {
    test: /\.svg$/,
    //issuer: {
    //  test: /\.[jt]sx?$/
    //},
    use: [
      {
        loader: 'svg-sprite-loader',
        options: {
          symbolId: '[name]'
        }
      },
      'svgo-loader'
    ]
  });

  return config;
}

module.exports = getCustomWebpackConfig;

@Krasnopir Does this work for you?

const getWebpackConfig = require('@nrwl/react/plugins/webpack');

function getCustomWebpackConfig(webpackConfig) {
  const config = getWebpackConfig(webpackConfig);

  // SVG fix
  const index = config.module.rules.findIndex(rule => rule.test.test('.svg'));
  config.module.rules.splice(index, 1, {
    test: /\.svg$/,
    //issuer: {
    //  test: /\.[jt]sx?$/
    //},
    use: [
      {
        loader: 'svg-sprite-loader',
        options: {
          symbolId: '[name]'
        }
      },
      'svgo-loader'
    ]
  });

  return config;
}

module.exports = getCustomWebpackConfig;

great. it work!!! much thanks!

@puku0x We need to update this for Next.js apps as well since it's managed by a different loader. Should track it separately here #2350.

The issuer is supposed to to prevent SVG imports within CSS files from being processed. Are your imports from .jsx and .tsx files?

I also realized that SVG support was not working from CSS files, not sure if this is the issue people were running into, but Nx should be using oneOf in the webpack rule for SVG so we use the first matched rule.

This PR will allow SVGs to be imported in CSS files. https://github.com/nrwl/nx/pull/2351

Let me know if there are any other issues. The sprites use case isn't officially supported right now, but we could look into it in the future.

@jaysoo I’m importing SVGs from not only .tsx but also .css because I’m using Ionic. #2351 will be a great help for me. Thank you!

Sounds good. We'll have this in the next release.

Closing this issue. If there are further use cases that aren't addressed please open a new issue. :)

Now Ionic React on Nx works out of the box!

/**
  app.tsx

  After cloning master,
  $ yarn create-playground
  $ cd tmp/nx/proj
  $ yarn nx g @nrwl/react:app
  $ yarn add @ionic/react
*/
import React from 'react';
import {
  IonApp,
  IonContent,
  IonHeader,
  IonPage,
  IonTitle,
  IonToolbar
} from '@ionic/react';

/* Core CSS required for Ionic components to work properly */
import '@ionic/react/css/core.css';

/* Basic CSS for apps built with Ionic */
import '@ionic/react/css/normalize.css';
import '@ionic/react/css/structure.css';
import '@ionic/react/css/typography.css';

/* Optional CSS utils that can be commented out */
import '@ionic/react/css/padding.css';
import '@ionic/react/css/float-elements.css';
import '@ionic/react/css/text-alignment.css';
import '@ionic/react/css/text-transformation.css';
import '@ionic/react/css/flex-utils.css';
import '@ionic/react/css/display.css';

export const App = () => {
  return (
    <IonApp>
      <IonPage>
        <IonHeader>
          <IonToolbar>
            <IonTitle>Title</IonTitle>
          </IonToolbar>
        </IonHeader>
        <IonContent>
          <p>Welcome to web!</p>
        </IonContent>
      </IonPage>
    </IonApp>
  );
};

export default App;

...except testing 😂

$ yarn test
yarn run v1.19.0
$ nx test web
 FAIL  apps/web/src/app/app.spec.tsx
  ● Test suite failed to run

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

    Details:

    /Users/puku0x/GitHub/nx/tmp/nx/proj/node_modules/ionicons/dist/ionicons/svg/ios-add.svg:1
    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M368.5 240H272v-96.5c0-8.8-7.2-16-16-16s-16 7.2-16 16V240h-96.5c-8.8 0-16 7.2-16 16 0 4.4 1.8 8.4 4.7 11.3 2.9 2.9 6.9 4.7 11.3 4.7H240v96.5c0 4.4 1.8 8.4 4.7 11.3 2.9 2.9 6.9 4.7 11.3 4.7 8.8 0 16-7.2 16-16V272h96.5c8.8 0 16-7.2 16-16s-7.2-16-16-16z"/></svg>
                                                                                             ^

    SyntaxError: Unexpected token <

      at ScriptTransformer._transformAndBuildScript (../../node_modules/@jest/transform/build/ScriptTransformer.js:537:17)
      at ScriptTransformer.transform (../../node_modules/@jest/transform/build/ScriptTransformer.js:579:25)
      at Object.<anonymous> (../../node_modules/ionicons/icons/imports/add.js:2:8)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        2.826s
Ran all test suites.
error Command failed with exit code 1.

@puku0x in order to get Ionic to work with the latest release, I still had to make webpack.config.js modifications that you posted. Also, were you able to get passed the linting error from using the require in the Webpack config? error Require statement not part of import statement @typescript-eslint/no-var-requires

@devinshoemaker Sorry for confusing you. I tested above example on the developing version of Nx which has not been released on npm yet. Therefore, you still need the webpack config for current version of Nx (v8.11.2). I think "Nx + Ionic React" should work in next release!

@devinshoemaker Talking about the linting error in webpack.config.js, I faced the same error but I ignored it and used require because '@nrwl/react/plugins/webpack' was not an ES module.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

markphip picture markphip  ·  3Comments

zpydee picture zpydee  ·  3Comments

kmkatsma picture kmkatsma  ·  3Comments

SWGeekPD picture SWGeekPD  ·  3Comments

vimalraj-a picture vimalraj-a  ·  3Comments