Webpack & Typescript image import
Asked Answered
D

6

66

I'm working on a React application and using Webpack & Typescript. I would like to use an image in one of the <img/> tags. However, I did not find the proper way to have access to the image files.

webpack.config.js:

 ...
 module: {
        rules: [
            ...
            {
                test: /\.(png|jpe?g|svg)$/,
                loader: 'file-loader',
                options: {
                    name: 'assets/[name].[ext]',
                }
            }
        ]

app.tsx:

...
render() {
    return <img src='/assets/logo-large.png' alt="logo"/>
}

When running the app, the assets/logo-large.png resource is not found.

Dissociation answered 26/4, 2017 at 15:26 Comment(0)
B
47

Setup Webpack file-loader, add declaration.d.ts, and voila!

after spending some time to figure out the solution, this is what I did...

Step 1

ensure that you have installed file-loader as a dev dependency by

npm install -D file-loader, if you use yarn yarn add -D file-loader

Step 2

add the loader corresponding to the file extension in the Webpack rules webpack.config.js, like this

module: {
    rules: [
      ...,
      {
        test: /\.(png|jpe?g|gif|jp2|webp)$/,
        loader: 'file-loader',
        options: {
          name: '[name].[ext]',
        },
      },
    ],
  },

Step 3

create an index.d.ts file next to your tsconfig.json file, actually you can name it whatever you want but you have to follow step 4.

Since Webpack now is going to handle several image extensions so you can add other image formats supported by file-loader

declare module '*.png';
declare module '*.jpg';

Step 4

go to your tsconfig.json file and add index.d.ts to the include array like this:

{
  "compilerOptions": {
    ...,
    "jsx": "react",
    "esModuleInterop": true,
    "target": "ES2020",
    "moduleResolution": "node"
  },
  "exclude": ["node_modules", "**/*.spec.ts", "**/*.test.ts"],
  "include": ["src", "index.d.ts"] /// <-- Like this!!
}

be aware that if you haven't defined an include array, typescript by default is going to add all the files on the root folder, if you just define one file and not the file that contains all your code it is not going to compile. I think is a good practice to have all your code within the src folder.

Voila!!

Boarish answered 17/2, 2021 at 23:1 Comment(2)
Yikes - this gave me errors in so many of my node_modulesDundee
fyi this is how it's done with create react app: github.com/facebook/create-react-app/blob/main/packages/…Harbird
D
71

Alternatively, in your custom_typings folder (if you have that), you can add a new import-png.d.ts file:

declare module "*.png" {
  const value: any;
  export default value;
}

So you can import an image using:

import myImg from 'img/myImg.png';

Alternatively, as reported by @mario-petrovic, you sometimes need to use a different export option as below (export = syntax). See here for the differences between the two approaches:

declare module "*.png" {
  const value: any;
  export = value;
}

In which case you probably need to import the image as:

import * as myImg from 'img/myImg.png';
Deodar answered 8/10, 2017 at 8:29 Comment(9)
Didn't know you could use wildcards in module declarations. That's super neat!Fillet
Just to add to this answer. I tried it with this solution and it didnt work. I made question here: https://mcmap.net/q/121264/-typescript-image-import/… . The problem was compile time error. When you correct export default value to export = value you will get typechecking correct and it will work. Thanks for leading to the final solution.Altogether
To use the image, I had to do <img src={myImg} />. Is that correct?Hermaphroditus
Great catch :). But I have a question, why typescript has to to this image loading thing different than js version of reactJS. @ErikVullingsPascha
@Pascha Not sure if I understand your question correctly, but TypeScript needs type information when you try to import something, and as an image does not have this (i.e. has no image.d.ts file), you need to supply that information yourself.Deodar
@ErikVullings even if typescript requires a type info for images. We are just passing the type any, so when we thing deeply, is not this redundantPascha
@Pascha TypeScript requires you to type things, even if you type it with any. Is this redundant, perhaps yes, but at least made you explicitly think about it.Deodar
If you, like me, came across this issue and thought that the part about making a new file for the declaration was an unimportant detail, read this old issue and see why you need a separate file to make the declare stick.Anastaciaanastas
Typescript: something that should be really simple, is now really complex. Not good.Miniaturize
B
47

Setup Webpack file-loader, add declaration.d.ts, and voila!

after spending some time to figure out the solution, this is what I did...

Step 1

ensure that you have installed file-loader as a dev dependency by

npm install -D file-loader, if you use yarn yarn add -D file-loader

Step 2

add the loader corresponding to the file extension in the Webpack rules webpack.config.js, like this

module: {
    rules: [
      ...,
      {
        test: /\.(png|jpe?g|gif|jp2|webp)$/,
        loader: 'file-loader',
        options: {
          name: '[name].[ext]',
        },
      },
    ],
  },

Step 3

create an index.d.ts file next to your tsconfig.json file, actually you can name it whatever you want but you have to follow step 4.

Since Webpack now is going to handle several image extensions so you can add other image formats supported by file-loader

declare module '*.png';
declare module '*.jpg';

Step 4

go to your tsconfig.json file and add index.d.ts to the include array like this:

{
  "compilerOptions": {
    ...,
    "jsx": "react",
    "esModuleInterop": true,
    "target": "ES2020",
    "moduleResolution": "node"
  },
  "exclude": ["node_modules", "**/*.spec.ts", "**/*.test.ts"],
  "include": ["src", "index.d.ts"] /// <-- Like this!!
}

be aware that if you haven't defined an include array, typescript by default is going to add all the files on the root folder, if you just define one file and not the file that contains all your code it is not going to compile. I think is a good practice to have all your code within the src folder.

Voila!!

Boarish answered 17/2, 2021 at 23:1 Comment(2)
Yikes - this gave me errors in so many of my node_modulesDundee
fyi this is how it's done with create react app: github.com/facebook/create-react-app/blob/main/packages/…Harbird
F
24

You need to require the image and then use that variable as the source, like so:

// At the top of the file, with all other imports/requires
const imageSrc = require('/assets/logo-large.png')

...

render() {
    return <img src={String(imageSrc)} alt="logo"/>
}
Fillet answered 23/5, 2017 at 9:29 Comment(1)
Only this one worked for me with typescript.Renettarenew
P
5

For Webpack 5, there are built-in Assets Modules that replace the old loaders.

If you're upgrading, make sure you aren't loading assets twice. If you are you can set the Assets Module type to javascript/auto, like so:

{
  test: /\.(png|jpg|gif)$/i,
  use: [
     {
       loader: 'url-loader',
       options: {
         limit: 8192,
       }
     },
   ],
   type: 'javascript/auto'
}

If you are starting with a fresh webpack config, there's no longer a need to install any of the loaders, nor use them in the config. Simply follow the steps mentioned by @Carlos, skipping step #1 and replacing the code in step #2 with the following:

rules: [ 
  {
    test: /\.(png|jpe?g|gif|jp2|webp)$/,
    type: 'asset/resource'
  },
//further sexiness
]
Phemia answered 13/3, 2022 at 7:50 Comment(5)
I'm still getting the "Cannot find module './image.png' or its corresponding type declarations.ts(2307)" error with this.Hord
@SamSverko I'd think maybe it's failing to register the module declaration for png files for some reason? From my working project, I am able to use the above webpack config, given that an "index.d.ts" exists in my root project directory with the with the following module declaration: declare module "*.png"; Phemia
@SamSverko Just for good measure, he's how you would import afterward: import CoolPicNoBrackets from './images/image.png'; and then consume like so: <img src={CoolPicNoBrackets} alt='Less cool but still important' />Phemia
I needed to add a declarations.d.ts inside my src/ folder. Adding to the project root didn't work for some reason...Hord
This didn't work for me - we are indeed using webpack 5Dundee
M
1

The copy-webpack-plugin might solve your problem as well, when you have a lot of images you can just serve them all from one central dist folder.

npm install --save-dev copy-webpack-plugin

    plugins: [
        ...
        ...
        new CopyWebpackPlugin([
            {from:'src/images',to:'images'} 
        ]), 
    ...
    ]

No you can simply at the relative path to your image tag:

<img src='images/your-image.png' />

Source: https://medium.com/a-beginners-guide-for-webpack-2/copy-all-images-files-to-a-folder-using-copy-webpack-plugin-7c8cf2de7676

Madcap answered 12/5, 2020 at 19:5 Comment(0)
T
-1

Actually, you do not require webpack for using webp images. This solution works for TypeScript and JavaScript based react apps as well. TypeScript gives an error if you try to import webp image as ReactComponent. So you should not import it as a component, but use only the source of image. Check this example:

import img from "./images/image.webp";

Now you can use this src in your tag like this.

<img src={img} />
Toluidine answered 5/6, 2021 at 5:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.