Question

How to Import SVG Files in TypeScript and TSX

Question

In TypeScript (.tsx) files, importing an SVG like this causes an error:

import logo from './logo.svg';

The TypeScript compiler reports:

[ts] Cannot find module './logo.svg'.

The SVG file itself is a normal SVG file:

<svg>...</svg>

The same import works in a .js file without any issue using the exact same statement. It seems like TypeScript may need to know the type of .svg files before allowing the import.

How can SVG imports be made to work correctly in TypeScript or TSX files?

Short Answer

By the end of this page, you will understand why TypeScript rejects SVG imports, why JavaScript often allows them, and how to fix the problem using module declarations. You will also see common SVG import patterns used in React and TypeScript projects.

Concept

TypeScript checks types at compile time. When you write:

import logo from './logo.svg';

TypeScript tries to answer a question before your app runs: what type does ./logo.svg export?

For JavaScript files, many bundlers such as Webpack, Vite, or Parcel already know how to handle image and SVG imports during the build. JavaScript does not care about static types, so the import can work at runtime.

TypeScript is stricter. It does not automatically know what a .svg file exports unless you tell it. As far as TypeScript is concerned, .svg is not a built-in module type like .ts or .js.

That is why you often need a module declaration such as:

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

This tells TypeScript:

  • any import ending in .svg is a valid module
  • the default export is a string

That matches many frontend setups where an imported SVG becomes a URL string.

In some React setups, SVG files can also be imported as components. In that case, the declaration is different because the imported value is not a string but a React component.

This matters in real programming because modern frontend apps often import assets directly:

  • icons
  • logos
  • background images
  • illustrations

TypeScript needs type information for all of them if you want a smooth developer experience.

Mental Model

Think of TypeScript as a security guard checking badges at a building entrance.

  • A .ts file has a badge TypeScript recognizes.
  • A .js file often gets through because runtime tooling handles it later.
  • A .svg file shows up with no badge TypeScript understands.

So TypeScript stops it and says: "I don't know what this module is."

A declaration file is like giving all .svg files an approved badge. Once you add that badge, TypeScript allows them in.

Take Quiz

Syntax and Examples

The most common fix is to add a declaration file.

1. Importing SVG as a file path string

Create a file such as src/types/images.d.ts or src/custom.d.ts:

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

Then this works:

import logo from './logo.svg';

export default function Header() {
  return <img src={logo} alt="Site logo" />;
}

Why this works

The declaration tells TypeScript that logo is a string, usually a URL produced by your bundler.

2. Importing SVG as a React component

Some toolchains support syntax like this:

import { ReactComponent as Logo } from './logo.svg';

export default function Header() {
  return <Logo />;
}

For that, you may need a declaration like:

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

  export const ReactComponent: React.FunctionComponent<
    React.SVGProps<SVGSVGElement>
  >;

  const src: string;
  export default src;
}

This supports both:

  • import logo from './logo.svg'
  • import { ReactComponent as Logo } from './logo.svg'

3. Make sure TypeScript can see the declaration file

Your tsconfig.json should include the folder where the .d.ts file exists. For example:

{
  "compilerOptions": {
    "jsx": "react-jsx"
  },
  "include": ["src"]
}

If the declaration file is inside src, TypeScript will usually pick it up automatically.

Step by Step Execution

Consider this example:

// src/custom.d.ts
declare module '*.svg' {
  const src: string;
  export default src;
}
// src/App.tsx
import logo from './logo.svg';

export default function App() {
  return <img src={logo} alt="Logo" />;
}

Here is what happens step by step:

  1. TypeScript reads App.tsx.

  2. It sees import logo from './logo.svg'.

  3. Without help, it does not know what .svg means as a module.

  4. It scans declaration files included in the project.

  5. It finds:

    declare module '*.svg' {
      const src: string;
      export default src;
    }
    
  6. Now TypeScript understands that any .svg import returns a string.

  7. So logo gets the type string.

  8. Passing that string into <img src={logo} /> is valid.

  9. Your bundler handles the actual SVG file during build or runtime.

TypeScript's job is only to understand the type shape. The bundler's job is to process the file itself.

Real World Use Cases

SVG imports are common in real applications.

Brand assets

import companyLogo from './assets/company-logo.svg';

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

Used in:

  • headers
  • authentication pages
  • dashboards

Icon systems

import searchIcon from './icons/search.svg';

<button>
  <img src={searchIcon} alt="Search" />
</button>

Used in:

  • buttons
  • menus
  • action bars

Inline SVG as components

import { ReactComponent as WarningIcon } from './warning.svg';

<WarningIcon />

Useful when you want:

  • CSS styling on SVG elements
  • dynamic sizing
  • changing fill or stroke via props or classes

Design systems

Teams often store many SVGs in a shared component or asset library. Type declarations make these imports predictable and type-safe across the codebase.

Take Quiz

Real Codebase Usage

In real projects, developers usually combine SVG declarations with a few practical patterns.

Centralized asset declarations

Instead of defining only .svg, teams often declare multiple asset types in one place:

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

declare module '*.png' {
  const src: string;
  export default src;
}

declare module '*.jpg' {
  const src: string;
  export default src;
}

This avoids repeated import errors for static assets.

Choosing one import style

Teams usually standardize on one of these:

  • SVG as URL string for simple images
  • SVG as React component for icons and styled graphics

That keeps code consistent.

Guarding against bundler mismatch

A declaration only satisfies TypeScript. Your build tool must also support the import style.

For example:

  • a declaration may say SVG imports return strings
  • but your bundler must actually process .svg files into asset URLs

If not, the code may type-check but still fail during build.

Reusable icon wrappers

In larger apps, developers often wrap SVG usage:

type IconProps = {
  src: string;
  alt: string;
};

export function Icon({ src, alt }: IconProps) {
  return <img src={src} alt={alt} width={16} height={16} />;
}

Then use it like:

import logo from './logo.svg';

<Icon src={logo} alt="Logo" />

This makes asset usage more consistent.

Common Mistakes

1. Adding the declaration file outside TypeScript's included paths

Broken setup:

{
  "include": ["src"]
}

If your declaration file is outside src, TypeScript may never see it.

How to avoid it:

  • place the .d.ts file inside an included folder
  • or update tsconfig.json

2. Writing the wrong module shape

Broken declaration:

declare module '*.svg' {
  export default React.Component;
}

This is not a valid way to describe the module.

Use a proper declaration instead:

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

3. Confusing TypeScript support with bundler support

Even if this compiles:

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

Your toolchain still needs to know how to load SVG files.

How to avoid it:

  • confirm your bundler supports asset imports
  • use the import style your framework expects

4. Using React component syntax without support

Broken code:

import { ReactComponent as Logo } from './logo.svg';

This only works in setups that specifically support named SVG component imports.

How to avoid it:

  • check your framework or bundler documentation
  • use default string imports if component imports are not configured

5. Forgetting to restart the TypeScript server

Sometimes the declaration file is correct, but the editor still shows errors.

How to avoid it:

  • restart the dev server
  • restart the TypeScript server in your editor
  • re-open the project if needed

Comparisons

Import styleWhat you getCommon usageType declaration
import logo from './logo.svg'A string URL or asset path<img src={logo} />const src: string; export default src;
import { ReactComponent as Logo } from './logo.svg'A React component<Logo />export const ReactComponent: React.FunctionComponent<...>
JavaScriptTypeScript
Bundler may allow SVG import without extra workTypeScript needs to know the module type
No static type checking for imported assetsStatic checking requires a .d.ts declaration
ConcernTypeScriptBundler
Understands imported module typeYesNot its main job
Processes actual SVG fileNoYes
Can fix compile-time type errorsYesNo
Can make the file load at runtimeNoYes
Take Quiz

Cheat Sheet

Import SVG as a string

declare module '*.svg' {
  const src: string;
  export default src;
}
import logo from './logo.svg';
<img src={logo} alt="Logo" />

Import SVG as a React component

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

  export const ReactComponent: React.FunctionComponent<
    React.SVGProps<SVGSVGElement>
  >;

  const src: string;
  export default src;
}
import { ReactComponent as Logo } from './logo.svg';
<Logo />

Key rules

  • TypeScript does not know .svg modules by default.
  • Add a .d.ts declaration file.
  • Make sure the file is included by tsconfig.json.
  • Your bundler must also support SVG imports.
  • Restart the TypeScript server if errors persist.

Common file names

  • src/custom.d.ts
  • src/types/assets.d.ts
  • src/images.d.ts

Quick fix checklist

  • Create a .d.ts file
  • Declare module '*.svg'
  • Export the correct type
  • Ensure tsconfig.json includes the file
  • Confirm bundler support

FAQ

Why does SVG import work in JavaScript but not TypeScript?

JavaScript does not do static type checking. TypeScript does, so it needs a declaration telling it what a .svg module exports.

Where should I put the *.d.ts file?

Place it inside a folder included by tsconfig.json, commonly inside src.

Should an imported SVG be typed as string or as a React component?

It depends on your setup. Use string when the SVG is imported as a file path. Use a React component type when your toolchain supports component-style SVG imports.

Is a module declaration enough to make SVG imports work?

Not always. It fixes TypeScript type errors, but your bundler must also know how to load SVG files.

Do I need to change tsconfig.json?

Only if TypeScript is not picking up your declaration file. Usually making sure the file is inside an included path is enough.

Can I declare multiple asset types the same way?

Yes. Many projects add declarations for .png, .jpg, .svg, and similar asset files.

Why is the error still visible after adding the declaration?

Your editor may need a TypeScript server restart, or the declaration file may be outside the included project paths.

Take Quiz

Mini Project

Description

Build a small React + TypeScript header component that displays a logo imported from an SVG file. This project demonstrates how to teach TypeScript what .svg imports mean and then use the imported asset safely inside TSX.

Goal

Create a working TSX component that imports an SVG file without TypeScript errors and renders it in the UI.

Requirements

  1. Create a TypeScript declaration file for .svg modules.
  2. Import an SVG file into a TSX component.
  3. Render the imported SVG using an <img> element.
  4. Ensure the declaration file is placed where TypeScript can find it.
Take Quiz

Keep learning

Related questions

View more