Unable to import svg files in typescript
Asked Answered
S

25

391

In typescript(*.tsx) files I cannot import svg file with this statement:

import logo from './logo.svg';

Transpiler says:[ts] cannot find module './logo.svg'. My svg file is just <svg>...</svg>.

But in .js file I'm able to import it without any issues with exact the same import statement. I suppose it has something to do with type of svg file which must be set somehow for ts transpiler.

Could you please share how to make this work in ts files?

Stu answered 23/6, 2017 at 8:54 Comment(8)
svg files are not javascript and can't be used as javascript modules are. You should load those files using an http request instead.Guido
Are you using Webpack? That's the only thing I've seen understand such an import statement. Perhaps Webpack is what's allowing this in your JavaScript, but it's not doing the same magic in TypeScript files. (I don't think that TypeScript itself knows what to do here.)Whimsical
If you are using Webpack, you'll probably need to share your Webpack config to get more help.Whimsical
Reading a little more on this, you can probably do const logo = require("./logo.svg"); or simply ignore the error. (I believe TS should still be outputting the right code.)Whimsical
thank you very much! require works good! In my case it has to be const logo = require("./logo.svg") as string;Stu
why, why Webpack/React had to complicate things ? Wouldn't it be simpler to just import anything with import. For a newbie like me, these things discourage me. Aren't we in 2020 where "auto-configuration" should be a norm ?Monumentalize
This is still an issue with CRA 5.x which is kind of insane to me. This issue is present with a fresh CRA-typescript project. It should be handled.Magen
@Monumentalize It's impossible to cater for all the different needs. CRA tries to achieve just this using some opiniated defaults. Use that. How should webpack know if you want to use typescript and use SVGs as components, instead of <img src={path} />?Paco
H
600

If you use webpack, you can do this by creating a custom types file.

Create a file named custom.d.ts with the following content:

declare module "*.svg" {
  const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
  export default content;
}

Add the custom.d.ts to tsconfig.json as below

"include": ["src/components", "src/custom.d.ts"]

Source: https://webpack.js.org/guides/typescript/#importing-other-assets

Hyssop answered 25/8, 2017 at 18:15 Comment(10)
Likely, you'd need to add it to the include section in tsconfig.json.Halifax
Thanks! I knew it must be included somewhere but I can't image where. Even I thought it was in tsconfig.json but I didn't know how to do it. Thank to your comment. I did a search and I found: "files": [ "custom.d.ts" ]Harve
You can get type-checking for the JSX component by typing the content: const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;Malina
Is it possible to have the custom.d.ts file work globally so the SVG can be in a different directory than the custom.d.ts file? I get an error "cannot find module" unless it's in the same directory.Glut
This doesn't import the content of the file in Angular, it imports the filename as a string. I need the content. How do we get the content of the file?Fang
@graham Why do we need custom.d.ts file when we already have react-app.d.ts. Already this declaration is defined here: github.com/facebook/create-react-app/blob/master/packages/… But it's not working? Any idea?Leporid
Caution, this workaround also suppresses completely invalid imports as long as they end with .svg.Grieg
This was working fine for me but I think it stopped working after updating my Gatsby (React) project or Typescript recently. I'm using Typescript 4.3.5 and Gatsby 3.1.1.Shamblin
If using docker, restart it after changesHufuf
This solution was originally not working for me until I changed the name of my *.d.ts to be exactly "custom.d.ts". Make sure this is the name!Khanna
S
83

Thanks smarx for pointing out use require(). So in my case it should be:

const logo = require("./logo.svg") as string;

which works fine in *.tsx files

Stu answered 23/6, 2017 at 10:12 Comment(7)
logo might be better named logoPath, because that's what it becomes.Insensible
@Insensible I think that can be debated. Also, it's called logo in the question, so it's a better answer to this specific question as it is.Palfrey
@Insensible honestly dude, the name of the variable is much aside the point, why get distracted over such a trivial thing?Antisyphilitic
I like this answer over creating a custom config rule. The only question is how well does this work in a production build if there is any side effect at all?Wishful
Having trouble making this work -- please clarify? #65205711Penna
For me, this made the TS2307 error go away, but now the import doesn't work. I have to choose error while working or no error but broken.Shamblin
this returns a @typescript-eslint/no-var-requiresPothunter
H
70
  • If you're using create-react-app 2+: docs

Add a custom.d.ts file (I created it on the root path of my src dir) with the correct type (thanks to RedMatt):

declare module '*.svg' {
  const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
  export default content;
}

Install svg-react-loader or some other, then:

  • Use it as the main svg loader
  • Or if you're migrating a codebase and don't want to touch the working part (JS) specify the loader on the import:
import MySVG from '-!svg-react-loader!src/assets/images/name.svg'

Then just use it as a JSX tag:

function f() { 
  return (<MySVG />); 
}
Hutton answered 24/1, 2020 at 18:22 Comment(2)
ESLint: Unexpected '!' in '-!svg-react-loader!src/assets/svg/bg.svg'. Do not use import syntax to configure webpack loaders.(import/no-webpack-loader-syntax)Shoestring
If you're using NextJs, this solution might not work for you. Look hereBlase
C
42

I scoured the internet looking for a solution to this issue. This stackoverflow question came up as the top lead, but none of the answers worked for me.

Finally, I was able to come to a solution by trying a few different techniques.

  1. Create an ./globals.d.ts file in the root of your project, at the same place/level your ./tsconfig.json is.

  2. Inside that ./globals.d.ts file, add this:

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

This properly imports the .svg as a string, which is an issue I noticed in the top-rated answer.

  1. Update your tsconfig.json with the following:
{
  "files": ["globals.d.ts"]
}

That's it - that got it to work in my case. I will note - this is in a VanillaJS app.

Carlock answered 17/4, 2022 at 17:58 Comment(0)
M
31

The solution that I found: In ReactJS project, in file react-app-env.d.ts you just remove the space in the comment such as:

Before

// / <reference types="react-scripts" />

After

/// <reference types="react-scripts" />

I hope to help you

Matchbook answered 16/5, 2020 at 15:55 Comment(4)
For people who are using create-react-app and configured eslint, this may solve the problemRadiobroadcast
I didn't find any explanation for this, yet in my case it seems to work. Any ideas why?Decompress
Here's an explanation on what it does: #60629374Bushwhack
This will be your issue if you have use CRA and then configure eslint or something (in my case vscode format) to format the react-app-env.d.ts file. It changes /// to // / due to the spaced-comment rule. You can change it back and add the ignore (/* eslint-disable spaced-comment */) and you should be goodDannettedanni
M
22

You can declare module for svgs the same way as create-react-app:

react-app.d.ts

declare module '*.svg' {
  import * as React from 'react';

  export const ReactComponent: React.FunctionComponent<React.SVGProps<
    SVGSVGElement
  > & { title?: string }>;

  const src: string;
  export default src;
}

see source

Madera answered 29/6, 2021 at 20:33 Comment(3)
I don't get it. Why does it define export const ReactComponent then uses export default src ? Shouldn't it be export default ReactComponent ?Wurster
Because CRA uses the SVGR loader under the hood, which does just that: npmjs.com/package/@svgr/webpack See also github.com/gregberge/svgr/issues/546Paco
worked for me with just export const ReactComponent = ..., thxBeaverbrook
P
17

Cannot find a module or its corresponding type declarations | Unable to import svg files in typescript

  1. Inside that ./custom.d.ts file, add this:
declare module '*.svg' {
  const content: string;
  export default content;
}
Phosphatize answered 18/8, 2022 at 12:49 Comment(0)
F
15

Solution without webpack and custom.d.ts

For me, none of the above solutions worked alone. Because I don't use the Webpack in my current project.

I investigated the outputs in the log, then the following way worked for me, without creating a file (custom.d.ts), changing the config, or installing a new dependency:

const logo: string = require("../assets/images/logo.svg").default;

<img src={logo} alt="logo" />

For svg format you need to add .default, but not for png format.

Filaria answered 3/2, 2022 at 0:33 Comment(1)
Thank you this one worked for me. Can you please explain why we should add .default for .svg files but not .png files?Nucleus
F
13

If you use vite, adding the following compilerOptions to tsconfig.json fixed the error for me:

  "compilerOptions": {
    "types": ["vite/client", "node"],
Fernferna answered 26/5, 2023 at 20:39 Comment(6)
I tried that, but got the following error in tsconfig.json: Cannot find type definition file for 'node'. The file is in the program because: Entry point of type library 'node' specified in compilerOptions. So I just deleted "node" from the list and it worked. Thanks!Nickinickie
i have both "vite/client", "node" and it worked for me.Fiftyfifty
Worked for me without "node", only with "types": ["vite/client"]. Thanks!Randi
There is something odd with vite -- the template project imported SVG no problem but my bigger project failed with this error. Probably a conflicting dependency/configuration? In any case this answer (@RaulRene's suggestion) fixed it.Rosalindrosalinda
Only vite/client is needed, which basically contains the same pseudo module declaration as the other answers.Nautical
As @RaulRene mentioned it works without needing of "none" and just with "types": ["vite/client"]. Thanks!Boston
O
11

If you're using the Create-React-App starter, make sure that the react-app-env.d.ts contains the line:

/// <reference types="react-scripts" />
Outguard answered 14/11, 2021 at 19:37 Comment(1)
This worked for me. something else I wanted to point out that the most straightforward way is to use create-react-app with typescript template which configures the project for you and works with svgs out of the box.Moth
Q
6

Hope! This will help someone.

Actually, I tried all the steps but one thing we have to understand, you have to create the custom.d.ts file into the corresponding SVG import folder.

ts config file

{
  "compilerOptions": {
    "target": "ES6",
    "jsx": "react",
    "module": "ESNext",
    "moduleResolution": "Node",
    "baseUrl": "./",
    "paths": {
      "@components/*": ["src/components/*"],
      "@styles/*": ["src/styles/*"],
      "@static/*": ["src/static/*"]
    },
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true
  },
  "include": ["src/**/*", "src/static/optional.d.ts"],
  "exclude": ["node_modules", "build"]
}

optional.d.ts

declare module '*.svg' {
    import * as React from 'react';

    export const ReactComponent: React.FunctionComponent<React.SVGProps<
        SVGSVGElement
    > & { title?: string }>;

    const src: string;
    export default src;
}

Finally the common export file:

import Logo from './images/logo.svg';
import BellDot from './images/bell-dot.svg';
import Logout from './images/logout.svg';
import pageNotFound from './images/page-not-found.png';

export {
    Logo,
    BellDot,
    pageNotFound,
    Logout
}

For a better idea:

enter image description here

Quintana answered 18/11, 2021 at 16:32 Comment(0)
S
5

I had the same issue while trying out a REACT + typescript tutorial.
What worked for me was the following import statement.

import * as logo from 'logo.svg'

Here are my dependencies in package.json.

  "dependencies": {
    "react": "^16.8.4",
    "react-dom": "^16.8.4",
    "react-scripts-ts": "3.1.0"
  },
  "devDependencies": {
    "webpack": "^4.0.0"
  }

Hope it helps someone.

Subtype answered 24/3, 2019 at 11:11 Comment(0)
T
4

There's an alternative way of doing this which we've implemented: make your SVGs components. I did this because it bugged me that I was using commonJS require statements alongside my imports.

Tachograph answered 20/6, 2018 at 7:37 Comment(0)
P
4

This comment helped if you want src functionality as well as being able to make it a react component. https://github.com/parcel-bundler/parcel/discussions/7910#discussioncomment-3690319

This goes in your globals.d.ts at the same level as your tsconfig.json

declare module '*.svg' {
export const ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>;
const src: string;
export default src;

}

Perry answered 17/4, 2023 at 15:27 Comment(1)
This solution worked well for me b/c without the inclusion of ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>; my compiler still complained about the key ReactComponent. Thanks!Blah
R
3
    // eslint-disable-next-line spaced-comment
/// <reference types="react-scripts" />

if you are using the puglin slint it may be that he has disabled thinking it was a comment but not to read the svg you need this type script module just disable the line and be happy

Riedel answered 25/5, 2020 at 1:10 Comment(0)
R
2

If none of the other answers work, try to restart your IDE (i.e. VS Code). In my case, that fixed it.

Referee answered 3/12, 2022 at 16:2 Comment(1)
I am new to React & TypeScript and VSCode, but at this early stage can attest to the extreme wisdom of the simple "Did you turn it off and back on again?" advice made famous on The IT Crowd!Papillose
M
1

For me, I had to include react-app-env.d.ts in my tsconfig*.json:

  "include": [
    "src/Router.tsx",        // my main entry point
    "src/global.d.ts",       // global stuff
    "src/react-app-env.d.ts" // react global stuff
  ]
Muumuu answered 12/3, 2022 at 15:43 Comment(0)
H
1

For me it worked when I put the following line in src/types/images.d.ts

declare module '*.svg';

and I'm importing images the following way

import { ReactComponent as WifiIcon } from '../../../assets/images/Wifi.svg';

in tsconfig.json

I have following complierOptions

"compilerOptions": {
    "typeRoots": ["node_modules/@types", "src/types"]
}

hope it helps someone. I use CRA the newest version.

Herbie answered 16/6, 2022 at 9:1 Comment(0)
B
1

If you're using NextJs ^13 with TypeScript, this should help you out:

npm install --save-dev @svgr/webpack

Since NextJs uses webpack, we need this plugin to process svg files and use those file import as React components. Learn more about svgr webpack plugin.

Then, open your next.config.* file, and add this webpack config to next config:

module.export = {
 // other config values

 webpack(config) {
   config.module.rules.push({
     test: /\.svg$/i,
     issuer: { and: [/\.(js|ts|md)x?$/] },
     use: ['@svgr/webpack'],
   });
   return config;
  }
};

Now, we need to override type definition for svg file content. It shouldn't be any but a proper component definition.

Create a custom .d.ts file anywhere in your project with the following content:

// custom.d.ts
declare module '*.svg' {
  const ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>;
  export default ReactComponent;
}

We also need to import this file in typescript config file - tsconfig.json:

"include": [
 "types/custom.d.ts",
 "next-env.d.ts"
]

Our custom declaration file should be included before next-env.d.ts. This order is important.

We should be good now.

import SvgLogo from 'path/to/logo.svg';

function App() {
   return (
      <SvgLogo />
   );
}
Blase answered 1/5, 2023 at 17:55 Comment(0)
E
0

If you're using Webpack >= v5 and would like to inline your image, this would perfectly work for you:

const logo = require('./logo.svg') as string;

function MyComponent() {
  return (
    <img src={logo} />  // <img src="data:image/svg+xml;base64,..." />
  );
}

In webpack.config.js add the following changes:

module: {
  rules: [
    // inline svg-files (see https://webpack.js.org/guides/asset-modules/)
    {
      test: /\.svg/,
      type: 'asset/inline'
    }
  ]
}

And yeah, also please note it might not work if your Content-Security-Policy header is set to strict options (e.g. only 'self') so it wouldn't allow inline images.

In that case you'll see a "broken" image on the web-page and a warning in the Dev Tools Console. To fix, just add data: into Content-Security-Policy setup, like this:

Content-Security-Policy: img-src 'self' data:
Eraser answered 24/3, 2023 at 22:52 Comment(0)
B
0

for me in react native "0.72.6",

Create an ./globals.d.ts file in the root of your project, at the same place/level your ./tsconfig.json is.

add this lines in globals.d.ts,

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

don't include in ./tsconfig.json

Use:

import SVG_google from '.SVG_google.svg';
Behka answered 5/11, 2023 at 6:41 Comment(0)
M
0

If you are using esbuild like me, you have to add loader to esbuild.build params:

import esbuild from 'esbuild'
await esbuild.build({
  loader: {
    '.svg': 'dataurl',
  },
})

Maudiemaudlin answered 22/11, 2023 at 19:45 Comment(0)
X
0

Here's the proper "svg.d.ts" file for Vue.js that will fix the error and also make it recognize the imported SVG as an actual <svg> element:

declare module '*.svg' {
  import type { SVGAttributes, DefineComponent } from 'vue';

  const content: DefineComponent<SVGAttributes>;
  export default content;
}
Xavler answered 1/3 at 15:7 Comment(0)
L
-1

If you use webpack, install svg-inline-loader, add the module in the webpack.config.js:

{
    test: /\.svg$/,
    loader: 'svg-inline-loader',
}

It works well after building.

If your IDE reports an interactively error, it can solved by adding //@ts-ignore:

//@ts-ignore
import logo from './logo.svg';

svg-inline-loader webpack docs

Louth answered 30/11, 2021 at 16:1 Comment(1)
This is a perfect workaround, but does not solve the issue. It just hides the problem. Have a look into the other solutions, especially stackoverflow.com/posts/45887328/revisions which gives an example with all steps included to finally solve that issue.Cystoid
I
-4

Import an SVG file in a CRA app

If you want to import an SVG file in a CRA app (create-react-app), without doing any config, you can use these methods:

TypeScript Files

import myIconFileName from './assets/images/my-icon.svg';
...    
<img src={myIconFileName} />

JS Files

import { ReactComponent as MyIcon } from "./assets/images/my-icon.svg";
...
<MyIcon />
Incompressible answered 3/12, 2021 at 11:8 Comment(2)
Yes this method obviously works in a js file, but not in a ts(x) file! The issue is importing svg file in "TypeScrip".Filaria
@HamidrezaSoltani Thank you for your tip, I edited it. Now the 1st approach works in the "ts" files and the 2nd approach only works in "js" files.Incompressible

© 2022 - 2024 — McMap. All rights reserved.