Problems with images in react application - reactjs

When using images in react, there is either a problem with typescript, or the image breaks on the site.
To solve the problem, I tried:
Add url-loader and file-loader to the webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const BUILD_PATH = path.resolve(__dirname, './build');
const SRC_PATH = path.resolve(__dirname, './src/');
const PUBLIC_PATH = path.resolve(__dirname, './public')
module.exports = {
entry: SRC_PATH + '/index.tsx',
output: {
path: BUILD_PATH,
filename: 'bundle.js',
},
mode: process.env.NODE_ENV || 'development',
resolve: {
modules: [path.resolve(__dirname, 'src'), 'node_modules'],
extensions: [ '.tsx', '.ts', '.js' ],
},
devServer: {
static: PUBLIC_PATH,
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.tsx?$/,
exclude: /node_modules/,
loader: 'ts-loader'
},
{
test: /\.(css|scss)$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
{
test: /\.module.css$/,
use: [
{
loader: "css-loader",
options: {
modules: true,
},
},
],
},
{
test: /\.(jpg|png|svg)$/,
loader: 'url-loader',
options: {
limit: 8192,
},
},
{
test: /\.(jpg|jpeg|png|gif|mp3|svg)$/,
use: ['file-loader']
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'public', 'index.html'),
}),
],
};
Import images as components
import React from 'react';
import logo from './header-logo.svg';
import styles from './Header.module.scss';
export const Header = () => {
return <header className={styles.header}>
<img src={logo} />
</header>
};
Create the images.d.ts file in the src/types directory
declare module "*.svg" {
const content: any;
export default content;
}
And I even tried svgr..
But nothing helped. If I delete the images.d.ts file, typescript cannot detect the module when importing. When using images.d.ts, vscode does not show errors, but the picture is not displayed in the browser, and instead of the normal path, something strange data:image/svg+xml;base64,ZXhwb3J0IGRlZmF1bHQgX193ZWJwYWNrX3B1YmxpY19wYXRoX18gKyAiZWMzYzM1Nzg3YTljZTMyMzE4M2NmMzM2Y2EzMDBkOTkuc3ZnIjs=
And just in case, I attach tsconfig.json
{
"compilerOptions": {
"baseUrl": "./",
"outDir": "./build/",
"noImplicitAny": true,
"module": "es6",
"target": "es5",
"jsx": "react",
"allowJs": true,
"allowSyntheticDefaultImports": true,
"moduleResolution": "node",
"plugins": [
{
"name": "typescript-plugin-css-modules"
},
],
},
}
I'm new to react so please don't judge strictly for stupid mistakes. I would appreciate any advice!

You can use svg-url-loader npm module. in webpack.config,js
{
test: /\.svg$/,
use: [
{
loader: "svg-url-loader",
options: {
limit: 10000,
},
},
],
},
in your component
// pass a correct path
import logo from './header-logo.svg';
<img src={logo} alt="" />

After some thought, I came to the conclusion that perhaps the problem occurred due to a loaders conflict. Leaving only the file-loader and deleting the url-loader, I was able to solve the problem. I hope my solution can help someone in the future.
My files now look like this:
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const SRC_PATH = path.resolve(__dirname, './src');
const BUILD_PATH = path.resolve(__dirname, './build');
const PUBLIC_PATH = path.resolve(__dirname, './public')
module.exports = {
entry: SRC_PATH + '/index.tsx',
output: {
path: BUILD_PATH,
filename: 'bundle.js',
},
mode: process.env.NODE_ENV || 'development',
resolve: {
modules: [SRC_PATH, 'node_modules'],
extensions: [ '.tsx', '.ts', '.js' ],
},
devServer: {
static: PUBLIC_PATH,
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.tsx?$/,
exclude: /node_modules/,
loader: 'ts-loader'
},
{
test: /\.(css|scss)$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
{
test: /\.module.css$/,
use: [
{
loader: "css-loader",
options: {
modules: true,
},
},
],
},
{
test: /\.(jpg|jpeg|png|gif|mp3|svg)$/,
use: ['file-loader']
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: PUBLIC_PATH + '/index.html',
}),
],
};
A imges.d.ts file for type resolution for typescript
declare module "*.jpg";
declare module "*.png";
declare module "*.svg";

Related

Webpack Error - You may need an appropriate loader to handle this file type,

I was trying to add webpack config in my react project, but i am facing issue as when i run the project , i get an error which says -
ERROR in ./src/index.js 14:2
Module parse failed: Unexpected token (14:2)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
|
| ReactDOM.render(
> <React.StrictMode>
| <App/>
| </React.StrictMode>,
here below is my configuration for webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: path.join(__dirname, "src", "index.js"),
output: {
path: path.resolve(__dirname, "dist"),
},
module: {
rules: [
{
test: /\.?js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env", "#babel/preset-react"],
},
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html",
filename: "./index.html",
}),
new MiniCssExtractPlugin(),
],
resolve: {
extensions: [".js", ".jsx"],
},
module: {
rules: [
{
test: /\.css$/i,
use: [
MiniCssExtractPlugin.loader, // instead of style-loader
"css-loader",
],
},
{
test: /\.(png|jp(e*)g|svg|gif)$/,
use: ["file-loader"],
},
{
test: /\.svg$/,
use: ["file-loader"],
},
],
},
};
Your configuration has 2 module keys. They conflict with each other. Merging them should resolve your issue:
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: path.join(__dirname, "src", "index.js"),
output: {
path: path.resolve(__dirname, "dist"),
},
plugins: [
new HtmlWebpackPlugin({
template: "./public/index.html",
filename: "./index.html",
}),
new MiniCssExtractPlugin(),
],
resolve: {
extensions: [".js", ".jsx"],
},
module: {
rules: [
{
test: /\.?js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env", "#babel/preset-react"],
},
},
},
{
test: /\.css$/i,
use: [
MiniCssExtractPlugin.loader, // instead of style-loader
"css-loader",
],
},
{
test: /\.(png|jp(e*)g|svg|gif)$/,
use: ["file-loader"],
},
{
test: /\.svg$/,
use: ["file-loader"],
},
],
},
};

Webpack collecting wrong src path

I think that the whole problem is in configuring the webpack, the images are successfully collected in the folder when building, but when importing ...
Somewhere I found a solution indicating the public path, but somehow it did not grow together
webpack.config.js
const path = require('path');
// const webpack = require('webpack');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: { main: './src/lib/index.js' },
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
libraryTarget: "umd",
library: "#compassplus/ui-mobicash"
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
url: true,
importLoaders: 1,
modules: true,
localIdentName: '[name]__[local]__[hash:base64:5]'
}
},
],
include: /\.module\.css$/,
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
],
exclude: /\.module\.css$/,
},
{
test: /\.(png|jp(e*)g|svg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[hash:12].[ext]',
outputPath: 'images/',
esModule: false,
},
},
],
},
]
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: 'index.css',
}),
// new HtmlWebpackPlugin({
// template: './public/index.html',
// }),
// new webpack.ProvidePlugin({
// "React": "react",
// }),
],
externals: {
react: 'react',
},
resolve: {
extensions: ['.js', '.jsx'],
},
}
Путь указанный в src / Path in the src:
src="images/809853c38dec.svg"
In the React component, I hook it up via import and pass it as an object
import imgLight from './img/theme-light.svg';
<img src={img} alt='Картинка' className={style.img}></img>
Solved the problem using url-loader:
{
loader: "url-loader",
options: {
limit: 8192,
name: "static/media/[name].[hash:8].[ext]"
},
}

How to import SASS file in React Webpack relative to project's root directory

I want to be able to import a SASS file in a React component relative to the project's root directory, as opposed to having to do it relative to the component.
I want to be able to do the following in the componenet:
import styles from "styles/popup.sass"
as opposed to
import styles from "../../styles/popup.sass"
I used the resolve options in order to be able to do this and it works for other file types (i.e. .js and .png), but it does not work for .sass files. I get the following error:
Cannot find module 'styles/popup.sass'
I'm not sure why this isn't working for SASS files and would really appreciate any help.
Project Structure:
src
- js
- popup
- greeting_componenet.jsx
- styles
- popus.sass
Webpack Config file
var webpack = require("webpack"),
path = require("path"),
fileSystem = require("fs"),
env = require("./utils/env"),
CleanWebpackPlugin = require("clean-webpack-plugin").CleanWebpackPlugin,
CopyWebpackPlugin = require("copy-webpack-plugin"),
HtmlWebpackPlugin = require("html-webpack-plugin"),
WriteFilePlugin = require("write-file-webpack-plugin");
// load the secrets
var alias = {};
var secretsPath = path.join(__dirname, ("secrets." + env.NODE_ENV + ".js"));
var fileExtensions = ["jpg", "jpeg", "png", "gif", "eot", "otf", "svg", "ttf", "woff", "woff2"];
if (fileSystem.existsSync(secretsPath)) {
alias["secrets"] = secretsPath;
}
var options = {
mode: process.env.NODE_ENV || "development",
entry: {
popup: path.join(__dirname, "src", "js", "popup.js"),
options: path.join(__dirname, "src", "js", "options.js"),
background: path.join(__dirname, "src", "js", "background.js")
},
output: {
path: path.join(__dirname, "build"),
filename: "[name].bundle.js"
},
module: {
rules: [
{
test: /\.css$/,
loader: "style-loader!css-loader",
include: [
path.join(__dirname, 'src'),
/node_modules\/(semantic-ui-css)/
],
},
{
test: /\.(scss|sass)$/i,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: {
localIdentName: "[path]___[name]__[local]___[hash:base64:5]",
},
}
},
{
loader: 'postcss-loader',
options: {
plugins: function() {
return [require('autoprefixer')]
}
}
},
'sass-loader'
],
exclude: /node_modules/
},
{
test: new RegExp('\.(' + fileExtensions.join('|') + ')$'),
loader: "file-loader?name=[name].[ext]",
include: [
path.join(__dirname, 'src'),
/node_modules\/(semantic-ui-css)/
],
},
{
test: /\.html$/,
loader: "html-loader",
exclude: /node_modules/
},
{
test: /\.(js|jsx)$/,
loader: "babel-loader",
exclude: /node_modules/
}
]
},
resolve: {
alias: alias,
extensions: fileExtensions.map(extension => ("." + extension)).concat([".jsx", ".js", ".css", ".sass"]),
modules: [
path.resolve(__dirname, 'src'),
'node_modules'
]
},
plugins: [
// clean the build folder
new CleanWebpackPlugin(),
// expose and write the allowed env vars on the compiled bundle
new webpack.EnvironmentPlugin(["NODE_ENV"]),
new CopyWebpackPlugin([{
from: "src/manifest.json",
transform: function (content, path) {
// generates the manifest file using the package.json informations
return Buffer.from(JSON.stringify({
description: process.env.npm_package_description,
version: process.env.npm_package_version,
...JSON.parse(content.toString())
}))
}
}]),
new HtmlWebpackPlugin({
template: path.join(__dirname, "src", "popup.html"),
filename: "popup.html",
chunks: ["popup"]
}),
new HtmlWebpackPlugin({
template: path.join(__dirname, "src", "options.html"),
filename: "options.html",
chunks: ["options"]
}),
new HtmlWebpackPlugin({
template: path.join(__dirname, "src", "background.html"),
filename: "background.html",
chunks: ["background"]
}),
new WriteFilePlugin()
]
};
if (env.NODE_ENV === "development") {
options.devtool = "cheap-module-eval-source-map";
}
module.exports = options;
You need to add aliases for styles.
Something like:
module.exports = {
//...
resolve: {
alias: {
styles: path.resolve(__dirname, 'src/styles/'),
js: path.resolve(__dirname, 'src/js/')
}
}
};

Resolve Relative Path from node_modules to Dist folder with Webpack

I'm using React component as an NPM Package. in the component, I have SCSS file
with url(../iamges/img..) path, but actually, the images folder located in the Dist folder, how can I point Webpack to take the relative path from node_modules and serve it from images folder located in the Dist?
located in node_modules =>
background: url('../images/some-icon.svg') no-repeat center center;
Webpack config:
const webpack = require('webpack');
const path = require('path');
module.exports = {
entry: './src/index.js',
devtool: 'inline-module-source-map',
output: {
path: path.resolve(__dirname, '/dist'),
filename: 'bundle.js',
publicPath: '/',
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.scss$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
{
loader: 'resolve-url-loader',
// options: {
// debug: true,
// root: path.join(__dirname, './dist/images'),
// includeRoot: true,
// absolute: true,
// },
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
sourceMapContents: false,
},
},
],
},
{
test: /\.css$/,
loaders: ['style-loader', 'css-loader'],
},
{
test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
use: {
loader: 'url-loader?name=/images/[name].[ext]',
options: {
limit: 10000,
},
},
},
],
},
resolve: {
extensions: ['.js', '.jsx'],
// modules: [path.resolve(__dirname, '/images'), 'node_modules'],
alias: {
'react-redux': path.resolve('./node_modules/react-redux'),
},
},
plugins: [new webpack.HotModuleReplacementPlugin()],
devServer: {
hot: true,
publicPath: '/dist/',
},
};
babel.config.js
module.exports = {
// presets: ['#babel/preset-env', '#babel/preset-react'],
plugins: [
'#babel/plugin-proposal-class-properties',
'#babel/plugin-proposal-export-default-from',
'#babel/transform-runtime',
],
sourceType: 'unambiguous',
presets: [
[
'#babel/preset-env',
{
targets: {
node: 'current',
},
},
],
'#babel/preset-react',
],
};
dist
-- images
-- index.html
ERROR:
ERROR in ./node_modules/comp/src/styles/details.scss (./node_modules/css-loader!./node_modules/resolve-url-loader!./node_modules/sass-loader/lib/loader.js??ref--5-3!./node_modules/compdetails/src/styles/details.scss)
Module not found: Error: Can't resolve '../images/icon.svg'
Anything referred through url('...') in css will be computed with reference to the path of deployed application (scss will not compute the path unless variable or function is not being used):
For example:
If your referred component SCSS module is having background: url('../images/some-icon.svg') no-repeat center center;
The final CSS compilation will be same (it is also because the component is not using any SCSS variables or functions to compute the final path).
So your application will always try to find that image as:
Example: http://localhost:3000/../images/some-icon.svg which is a problem.
(.. is referred as parent directory)
If you try to run your app with some sub-url (also known as sub context) as http://localhost:3000/sub-url/ and you keep your images parallel to sub-url folder it will automatically work.
-- /
|
|-- sub-url
|
|-- index.html
|-- images
|
|-- some-icon.svg
Another option can be override the component SCSS with yours.
You already found the solution to use resolve-url-loader, but in this case you need to import the component's scss file into you scss.
so your webpack config should look like:
const webpack = require('webpack');
const path = require('path');
module.exports = {
entry: './src/index.js',
devtool: 'inline-module-source-map',
output: {
path: path.resolve(__dirname, '/dist'),
filename: 'bundle.js',
publicPath: '/',
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.scss$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
// CHANGE HERE
{
loader: 'resolve-url-loader',
options: {
root: '/images', // considering all your images are placed in specified folder. Note: this is just a string that will get as prefix to image path
includeRoot: true,
absolute: true,
},
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
sourceMapContents: false,
},
},
],
},
{
test: /\.css$/,
loaders: ['style-loader', 'css-loader'],
},
{
test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
use: {
loader: 'url-loader?name=/images/[name].[ext]',
options: {
limit: 10000,
},
},
},
],
},
resolve: {
extensions: ['.js', '.jsx'],
// modules: [path.resolve(__dirname, '/images'), 'node_modules'],
alias: {
'react-redux': path.resolve('./node_modules/react-redux'),
},
},
plugins: [new webpack.HotModuleReplacementPlugin()],
devServer: {
hot: true,
publicPath: '/dist/',
},
};
I hope it helps.

Create a React component lib

I'm trying to create a library with reusable react components. I'm using React, Typescript, Sass and Webpack.
My problem is : when I'm using my components from the result of the webpack build, there is no css with them although I have an index.css file with everything I need.
It seems like the css is not used by the output.
Here is my webpack.config.json :
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const path = require('path');
module.exports = {
mode: 'production',
entry: path.resolve(__dirname, '../src/index.ts'),
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'index.js',
library: 'whatever',
libraryTarget: 'umd',
umdNamedDefine: true
},
devtool: 'source-map',
externals: {
react: 'react',
'react-dom': 'react-dom',
},
module: {
rules: [
{
test: /\.tsx?/,
loader: 'ts-loader',
options: {
configFile: path.resolve(__dirname, '../tsconfig.build.json')
},
exclude: /node_modules/
},
{
test: /\.s(a|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[name]__[local]___[hash:base64:5]',
camelCase: true,
sourceMap: false
}
},
{
loader: 'sass-loader',
options: {
sourceMap: false
}
}
]
},
]
},
resolve: {
modules: ["node_modules", "src"],
extensions: ['.tsx', '.ts', '.scss', '.js']
},
plugins: [
new MiniCssExtractPlugin({
filename: 'index.css',
chunkFilename: '[id].[hash].css',
}),
]
};
Here is my tsconfig.json:
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"lib": [ "es2015", "dom" ],
"outDir": "./dist",
"sourceMap": true,
"moduleResolution": "node",
"declaration": true,
"strict": true,
"jsx": "react"
},
"include": [
"src"
],
"exclude": [
"node_modules",
"src/**/*.stories.tsx",
"src/**/*.test.tsx"
]
}
EDIT 1:
As suggested by Muhammad Mehar I changed the loader: [MiniCssExtractPlugin.loader, ... to use: [... but it didn't work
Here is how I'm trying to use my lib :
somewhereInTargetApp.ts
import { MyComponent } from 'MyLib';
export const OtherComponent = (props) => {
return (
<MyComponent someProp={'Hello world'}/>
)
};
And in my lib I have some index.ts file which exports the component.
Everything works fine for the component except that there is no css with it
Rule.loader is a shortcut to Rule.use: [ { loader } ].
Passing a string (i.e. loader: 'style-loader' ) is a shortcut to the loader property (i.e. use: [ { loader: 'style-loader '} ])
Rule.use can be an array of UseEntry which are applied to modules. Each entry specifies a loader to be used.
Passing a string (i.e. use: [ 'style-loader' ]) is a shortcut to the loader property (i.e. use: [ { loader: 'style-loader '} ])
Loaders can be chained by passing multiple loaders, which will be applied from right to left (last to first configured)
Try this:
module: {
rules: [
{
test: /\.tsx?/,
loader: 'ts-loader',
options: {
configFile: path.resolve(__dirname, '../tsconfig.build.json')
},
exclude: /node_modules/
},
{
test: /\.s(a|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[name]__[local]___[hash:base64:5]',
camelCase: true,
sourceMap: false
}
},
{
loader: 'sass-loader',
options: {
sourceMap: false
}
}
]
},
]},
I hope it help you.
In the end I switched to CSS in JS with aphrodite
This is not what I wanted to do but it will do the work for now
If someone has an answer to my problem I would be happy to test it

Resources