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
.svgis 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
.tsfile has a badge TypeScript recognizes. - A
.jsfile often gets through because runtime tooling handles it later. - A
.svgfile 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.
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:
-
TypeScript reads
App.tsx. -
It sees
import logo from './logo.svg'. -
Without help, it does not know what
.svgmeans as a module. -
It scans declaration files included in the project.
-
It finds:
declare module '*.svg' { const src: string; export default src; } -
Now TypeScript understands that any
.svgimport returns astring. -
So
logogets the typestring. -
Passing that string into
<img src={logo} />is valid. -
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.
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
.svgfiles 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.tsfile 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 style | What you get | Common usage | Type 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<...> |
| JavaScript | TypeScript |
|---|---|
| Bundler may allow SVG import without extra work | TypeScript needs to know the module type |
| No static type checking for imported assets | Static checking requires a .d.ts declaration |
| Concern | TypeScript | Bundler |
|---|---|---|
| Understands imported module type | Yes | Not its main job |
| Processes actual SVG file | No | Yes |
| Can fix compile-time type errors | Yes | No |
| Can make the file load at runtime | No | Yes |
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
.svgmodules by default. - Add a
.d.tsdeclaration 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.tssrc/types/assets.d.tssrc/images.d.ts
Quick fix checklist
- Create a
.d.tsfile - Declare
module '*.svg' - Export the correct type
- Ensure
tsconfig.jsonincludes 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.
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
- Create a TypeScript declaration file for
.svgmodules. - Import an SVG file into a TSX component.
- Render the imported SVG using an
<img>element. - Ensure the declaration file is placed where TypeScript can find it.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.