ChatGPT解决这个技术问题 Extra ChatGPT

Unable to import svg files in typescript

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?

svg files are not javascript and can't be used as javascript modules are. You should load those files using an http request instead.
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.)
If you are using Webpack, you'll probably need to share your Webpack config to get more help.
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.)
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 ?

N
Niket Pathak

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: any;
  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


Likely, you'd need to add it to the include section in tsconfig.json.
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" ]
You can get type-checking for the JSX component by typing the content: const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
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.
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!
A
AngryBoy

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


logo might be better named logoPath, because that's what it becomes.
@DharmaTurtle 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.
@DharmaTurtle honestly dude, the name of the variable is much aside the point, why get distracted over such a trivial thing?
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?
Having trouble making this work -- please clarify? stackoverflow.com/questions/65205211/…
A
Allenaz

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 />); 
}

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)
j
jilvanx

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


For people who are using create-react-app and configured eslint, this may solve the problem
I didn't find any explanation for this, yet in my case it seems to work. Any ideas why?
Here's an explanation on what it does: stackoverflow.com/questions/60628874/…
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 good
k
kimbaudi

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


I don't get it. Why does it define export const ReactComponent then uses export default src ? Shouldn't it be export default ReactComponent ?
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/546
a
adstwlearn

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.

Create an ./globals.d.ts file in the root of your project, at the same place/level your ./tsconfig.json is. 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.

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.


O
Oleksandr Danylchenko

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" />

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.
K
Kiran Mohan

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"
  },

Hope it helps someone.


e
endymion1818

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.


H
Hamidreza Soltani

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.


B
Breno Melo
    // 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


P
Premji

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:

https://i.stack.imgur.com/6vhvm.png


E
Emad Armoun

Import a SVG file in a CRA app

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

Method 1

import { ReactComponent as MyIcon } from "./assets/images/my-icon.svg";
...
<MyIcon />

Method 2

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

Yes this method obviously works in a js file, but not in a ts(x) file! The issue is importing svg file in "TypeScrip".
c
cloxnu

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


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.
J
Josh M.

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
  ]

R
Robert

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.