Displaying a static image using React, Typescript and Webpack
Asked Answered
S

7

16

I'm attempting to display an image in a React component as part of a project using webpack and webpack-dev-server.

So far I have completed the following steps:

  • Used npm to install file-loader
  • Updated webpack.config.js to add a loader for image files
  • Imported the image I want into my component
  • Used the import in my img tag

Having taken these steps, webpack fails to compile with a 'cannot find module' error:

ERROR in [at-loader] ./src/components/App.tsx:4:26
    TS2307: Cannot find module '../images/kitten-header.jpg'.

My folder structure is as follows:

/dist
   /images
      kitten-header.jpg
   bundle.js
   bundle.js.map
/node_modules
   (content ignored for brevity)
/src
   /components
      App.tsx
   /images
      kitten-header.jpg
   /styles
      App.less
   index.tsx
index.html
package.json
tsconfig.json
webpack.config.js 

The new loader that I added to my webpack.config.js is:

test: /\.(jpe?g|gif|png|svg)$/, loader: "file-loader?name=./images/[name].[ext]"

I've imported the image file, in App.tsx, like this:

import kittenHeader from '../images/kitten-header.jpg';

...and used the import in an img tag like this:

<img src={ kittenHeader } />

Note: Full text of webpack.config.js and App.tsx were provided until I got a little bit closer to the answer and realized they weren't relevant (see update 1).

I assume I'm making some very trivial error with regards to the relative path in the import. As you can imagine, I've tried various alternatives.

Can anyone provide some insight?

For reference, as I'm continuously hitting articles and questions relating to the wrong version of webpack, here are my versions:

  • React 15.5.4
  • Webpack 2.6.1
  • Webpack-dev-server 2.4.5
  • TypeScript 2.3.2


Update 1: 2017.06.05
So, looking at this SO question and this post on Medium, I've been able to identify that the problem lies not with how I've used webpack, but with that fact that I'm using TypeScript. The error is a typescript error caused by the fact that the typescript compiler doesn't know what a .jpg file is.

Apparently, I'm going to need to provide a d.ts file or use a require statement.

Almost there...

Selfeffacing answered 1/6, 2017 at 15:54 Comment(8)
Do you by any chance have a source repository we could use to reproduce the problem? That would be very handy.Justifiable
If you are using webpack, you can install @types/webpack-env instead and fall back require instead of import. Unlike import, the string you pass to require doesn't get resolved by TypeScript. Of course, using a *.d.ts is fine, tooHaemostasis
Thank you @EvanSebastian, you're comment came in just as I was writing up my answer. As you can see, my answer is incomplete. Do you know how to write a d.ts for the jpg? Is it possible to create a module to cover *.jpg or would a d.ts be required for every image file?Morisco
I also notice you've recommended @types/webpack-env , whilst I've used @types/node. The node types do work. What is the difference between these two, in terms of getting 'require' to work? I assume they are simply two d.ts definitions of the same underlying 'require' implementation?Morisco
@types/webpack-env and @types/node are different, you use @types/node when you need to write NodeJS modules to make standard library typings available. @types/webpack-env is used when you are in a webpack bundled project. For instance, require.ensure will be available if you use @types/webpack-envHaemostasis
Ok, thank you, I'll switch to @types/webpack-env.Morisco
In case you prefer to use .d.ts, this is the syntax github.com/Microsoft/TypeScript/wiki/… You want export default to have type string in this caseHaemostasis
Let us continue this discussion in chat.Morisco
T
16

With regular require, this is how I use it in a tsx file:

const logo = require('../assets/logo.png');

...

<img alt='logo' style={{ width: 100 }} src={String(logo)} />

Hope this helps. importing did not work for me, either.

Topping answered 1/10, 2017 at 13:36 Comment(0)
M
4

This is how I get it working:

// This declaration can be move in some common .d.ts file, will prevent tslint from throwing error
declare function require(path: string);

const SampleComponent = () => (
  <div>
    <img src={require('./styles/images/deadline.png')} alt="Test" />
  </div>
);

The way you proposed in your answer will end up writing modules declaration for each image file being placed in components.

Misfeasance answered 22/6, 2017 at 21:14 Comment(0)
S
2

The error, TS2307: Cannot find module '../images/kitten-header.jpg', is highlighting that the TypeScript compiler does not understand the import of a .jpg file.

The webpack configuration is fine, as evidenced by the image file being copied to the dist folder successfully despite the compile error.

There are two ways that we can resolve the issue.

Firstly, we could bypass the TypeScript import by using a 'require' statement. To do this, the import...

import kittenHeader from '../images/kitten-header.jpg';

...can be replaced with a require...

const kittenHeader = require('../images/kitten-header.jpg');

...and that should be all that's needed.

Note that, in my case I also needed to install some typings to support the 'require' statement. Without these, I was getting a TS2304: Cannot find name 'require' error.

Originally I used @types/node, as described in this SO answer, but @EvanSebastian pointed out that node is intended to support writing NodeJS modules, which is not what I'm doing (see comments on the question).

I tried @types/webpack-env, as suggested, but this resulted in a TS2322: Type '{ src: {}; }' is not assignable to type 'HTMLProps<HTMLImageElement>'. error against the src property of my image tag.

Most recently, I've switched to using @types/requirejs, which I'm hoping is focused enough to avoid including a load of inappropriate additional types. It is currently working:

npm install @types/requirejs --save-dev

Alternatively, we could keep the import and provide a d.ts file with type information declaring an appropriate module. I've not been able to discover exactly what this file would look like.

Selfeffacing answered 5/6, 2017 at 13:6 Comment(0)
R
0

Add a typescript definition file anywhere in your src directory and (name it something like image.d.ts) Export a module definition for jpeg files like so:

export module "*.jpg";
Reassure answered 15/4, 2020 at 21:13 Comment(0)
T
0

Try my code, in any React-Typescript component you want

import * as logo from './assests/icon.jpg';

const img = logo.default;

const Comp = ()=> {
    return <img src={img} alt="image" className="" />
}
Turnstile answered 11/5, 2021 at 12:42 Comment(1)
Code only answers are ill-advised.Tombouctou
A
0

const logo = require('../../../assets/img/logo.svg')

 <img src={logo.default} style={{ width: "100%", height: "50px" }} alt="" />

Anticipant answered 14/8, 2021 at 21:37 Comment(1)
Consider adding an explanation of your code to help readers understand how it addresses the question. When answering a question with several other answers, it also is useful to explain how your answer differs from or improves on the others.Atomic
G
-1

In addition to mcku's answer you can also use imports this way:

// @ts-ignore
import * as kittenHeader from '../images/kitten-header.jpg';
...
<img alt='logo' style={{ width: 100 }} src={logo} />

Although you have to ignore the warning.

Gunnel answered 12/1, 2019 at 21:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.