I Launch project with create-react-app. No response even if img path is specified on React APP.
Image is displayed when import statement is used
// not working (1)
<img src={"../img/hoge.png"} />;
//working (2)
import Hoge from "../img/hoge.png";
<img src={Hoge} />
I want to user pattern (1).
Please tell me the solution orz......
Use require for dynamic imports, path like "../img/hoge.png" is not available in runtime because Webpack generates data URI in build time.
import hoge from '../img/hoge.png'; // Tell webpack this JS file uses this image
console.log(hoge); // /hoge.84287d09.png
// For dynamic imports
<img src={require("../img/hoge.png")} />
See Adding images in CRA docs.
If you have a list of potential images at build time, you can use the dynamic import function to load them, although this will return a promise like so:
export class ImageLoader extends React.Component {
constructor(props: any){
super(props);
this.state = {
imagePath: undefined
};
}
render() {
if(this.state.imagePath)
return <img src={this.state.imagePath} />; // Render the image
import(/* webpackMode: "eager" */`../imgs/${this.props.imageName}.png`) // Bundle all paths of images into the bundle
.then((imagePath: string) => {
this.setState({ imagePath}); // Set the new state with the loaded image path
});
return null; // Don't render anything right now since the image hasn't loaded yet.
}
}
This will bundle ALL png images under the imgs directory, use a switch statement with all possible names if that is too much.
See Webpack Documentation and How does Dynamic Import in webpack works when used with an expression?
Related
when I want to export my nextjs app, it says that I cannot export my images on static websites.
Error: Image Optimization using Next.js' default loader is not compatible with next export.
Possible solutions:
- Use next start to run a server, which includes the Image Optimization API.
- Use any provider which supports Image Optimization (like Vercel).
- Configure a third-party loader in next.config.js.
- Use the loader prop for next/image.
How can I make it so that it does ?
Is there a way for me to simply tell it to render images statically ? I dont want to go throught other onlines images loaders..
I created a npm module so that we can use the Next.js <Image/> component with optimized images while using the static export functionality.
https://www.npmjs.com/package/next-image-export-optimizer
The library wraps the <Image /> component of Next.js and automatically creates optimized images using sharp.js at export time.
It uses a custom loader to create a srcset for the <img /> that the <Image /> component of Next.js creates. Then at build/export time, the images inside the public/images folder (as an example) are optimized with sharp.js and copied into the build folder.
You need to set up a custom image loader in Next.js
In your next.config.js file, add this property to the export:
images: {
loader: "custom"
}
And make a script called loader.js that exports this:
function imageLoader({ src }) {
return `/images/${src}`; // REPLACE WITH YOUR IMAGE DIRECTORY
}
module.exports = imageLoader;
For each Image component, set the loader prop manually:
const imageLoader = require("PATH TO loader.js");
<Image loader={imageLoader} />
I'm going to add onto skara9's answer because it didn't quite work for me. I found a thread on github discussing it and the answer there worked for me. It just wraps around the NextJS image component and works pretty flawlessly for me.
// components/Image.js
import NextImage from "next/image";
// opt-out of image optimization, no-op
const customLoader = ({ src }) => {
return src
}
export default function Image(props) {
return (
<NextImage
{...props}
loader={customLoader}
/>
);
}
Make sure you change your imports and update your next.config.js
import Image from '../components/Image.js'
Is it possible to conditionally import assets when creating a React app using create-react-app? I'm aware of the require syntax - example:
import React from "react";
const path = process.env.REACT_APP_TYPE === "app_1" ? "app_1" : "app_2";
const imagePath = require(`./assets/${path}/main.png`);
export default function Test() {
return (
<img src={imagePath} alt="" />
);
}
This however bundles all my assets no matter what.
It will load the proper image, but it will still bundle all the files together in the final build.
When I look in the dev tools for the final build, I can see all the assets there even though I only wanted to load the assets for app_1.
Am I forced to touch the webpack config, if so, what should I change? or is there another way?
In the days when React didn't exist we didn't put assets into our JS files. We let the CSS to decide, what assets to load for what selectors. Then you could simply switch a corresponding class on or off for a corresponding element (or even the whole page) and viola it changes color, background, or even a form. Pure magic!
Ah. What times these were!
All above is true and I do not understand why would anyone do or recommend doing it differently. However if you still want to do it (for any reason) - you can! Latest create-react-app comes with out-of-the-box support for lazy loading of arbitrary components via dynamic importing and code splitting. All you need to do is use parenthesized version of the import() statement instead of the regular one. import() takes in a request string as usual and returns a Promise. That's it. Source code of the dynamicaly requested component won't be bundled in, but instead stored in separate chunks to be loaded on demand.
Before:
import OtherComponent from './OtherComponent';
function MyComponent() {
return (
<div>
<OtherComponent />
</div>
);
}
After:
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<OtherComponent />
</div>
);
}
Notice how function MyComponent part is identical.
For those wondering if it is tied to CRA or React, it's not. It's a generic concept that can be used in vanilla JavaScript.
You will need to use webpack (or other bundler.) The code is not being run when it's bundled, so the compiler has no way of knowing which branch of logic to follow (app_1 or app_2). Therefore you have to get into the bundler's logic in order to achieve your goal.
However, this isn't as scary as it seems since webpack has built in capability to do this (no 3rd parties required...)
I would look into using webpack.providePlugin
(https://webpack.js.org/plugins/provide-plugin)
or its sibling DefinePlugin
(https://webpack.js.org/plugins/define-plugin)
(I'm afraid these examples are off the top of my head, so it's very unlikely they'll work on first pass.)
Examples:
Both will require a provider module...
// in path/provider.js
module.exports = {
live: '/path/to/live/image',
dev: '/path/to/dev/image'
}
Provide Plugin Example
// in webpack
new webpack.ProvidePlugin({
imagePath: [
'path/provider', // the file defined above
process.env.ENVIRONMENT // either 'dev' or 'live'
]
}),
// in code
export default function Test() {
return (
<img src={imagePath} alt="" />
);
}
Define Plugin example:
// in webpack
new webpack.DefinePlugin({
'process.env.ENVIRONMENT': JSON.stringify(process.env.ENVIRONMENT)
});
// in code
var providers = require('path/provider'); // same path provider as above
export default function Test() {
return (
<img src={providers[process.env.ENVIRONMENT]} alt="" />
);
}
In both cases the bundler is forced to collapse your variable to an actual literal value at compile time - before bundling has taken place. Since you have now collapsed the logical path down to a single option, it is now free to only bundle the relevant assets.
You can't do this with default CRA settings.
Because if your dynamic require or dynamic import path is not static, webpack won't be able to determine which assets to include in the final build folder, therefore, it will grab everything from your ./src folder, and put them all to your build folder.
There is a way to do it with default CRA settings
You can add to .env something like
REACT_APP_SKIN=1
Put your skin assets in public/css1, public/css2 etc. And include them in public/index.html using code like
<link href="/css%REACT_APP_SKIN%/theme.css" rel="stylesheet">
I'm trying to create a custom component for my markdown that accepts an image source. I am unable to display the image via the custom component because the image is not found because it doesn't exist
I also realised the image path is generated by GatsbyJS and I have no idea how to retrieve the path of the image in markdown.
I do have a custom component that holds some text but I couldn't do the same thing for images.
Here is a simple markdown with a title and a few words.
index.md
---
title: ToDoApp
---
Hi this is my todoapp app. Below is a bunch of screens
<imageholder src='./screen1.png'></imageholder>
![Image from Gyazo](./screen1.png) <!-- it displays... -->
I've created a custom component named imageholder where it holds some logic (in a near future...) in displaying the image
ImageHolder.js
import React from "react"
export default class ImageHolder extends React.Component {
render() {
return (
<img src={this.props.src} alt="Logo"/>
)
}
}
project-post.js
const renderAst = new rehypeReact({
createElement: React.createElement,
components: {
"imageholder": ImageHolder
},
}).Compiler
And I received this...
This is really tricky since (AFAIK) you can't pass props from page component to custom component with rehype-react. I think you'd need to do something similar to gatsby-remark-images, which locates the images' paths and set them.
I wrote this plugin that mimics gatsby-remark-images, but for custom components like in your case.
Here's the default setting, you can override the component name and pass in additional image transformation options.
// gatsby-config.js
module.exports = {
plugins: [
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
{
resolve: `gatsby-remark-custom-image-component`,
options: {
// plugin options
componentName: 'image-wrapper',
imagePropName: 'src',
sharpMethod: 'fluid',
// fluid's arguments, see gatsby-plugin-sharp docs
quality: 50,
maxWidth: 800,
}
},
],
},
},
Then use it in your markdown:
<image-wrapper src='./hero.jpg'></image-wrapper>
And get the image props in your custom component.
//src/components/ImageWrapper.js
import React from 'react'
// the result of sharp's image transformation will be passed directly to this component.
// so if you use `fluid` as `sharpMethod`, you'll get
// src, srcSet, base64, aspectRatio, srcSetType, sizes, density, originalImage.
// Please refer to `gatsby-plugin-sharp` docs.
const ImageWrapper = ({ src, srcSet }) => <img src={src} srcSet={srcSet} />
export { ImageWrapper }
The issue is that props are passed as strings to rehype - the component doesn't receive the asset hashed value when the markdown is processed and built by Gatsby. So, the prop isn't the same as the image tag's src once you build the site, and it's not finding the asset hashed file.
This plugin, Gatsby Remark Copy Linked Files, moves your referenced asset files to a public folder, and passes the correctly hashed asset path, but by default only for img, a, audio, and video tags (not for custom components).
To solve for this, move the plugin from node_modules into a /plugin folder in the project root, and add the desired custom components and props at this line. In your case, it looks like it would be:
// Handle a tags.
extractUrlAttributeAndElement($(`a[href]`), `href`).forEach(processUrl)
// Manually added custom tags
extractUrlAttributeAndElement($(`imageholder[src]`), `src`).forEach(processUrl)
Obviously this would be better served as an option for the plugin in a configuration block in gatsby-config, but this worked for me in a pinch.
I am attempting to load an image with the src of the image being stored in the this.props.src. The this.props.src outputs the correct name of the img file I would like to load: "koolImg". I am importing the file by: import koolImg from '../img/koolImg.png'. This is the current setup: <img src={this.props.src} alt="noLoad"/>. However, no image is loaded, just the "alt" value is displayed. What am I missing? I am new to React.
I created this react app using the npx create-react-app koolImgApp command, thus using webpack. Running on React 16.4.1 and using Google Chrome 67.
koolImg is just a string which can't be used directly as a src attribute of an img element.
You could map this to the image you have imported by storing the imported image variable in an object, and then accessing that with your src prop string:
Example
import React, { Component } from 'react';
import koolImg from '../img/koolImg.png';
const images = {
koolImg
};
class ImgSrc extends Component {
render() {
return <img src={images[this.props.src]} />;
}
}
I am creating a React component that randomly cycles through a large number of background images. From my understanding, I could ignore Webpack here and just dynamically generate the require/import statements and pass them to the component.
However, if I want Webpack to use a loader and generate hashes, etc - I haven't found a solution.
This question is similar, but the responses don't address the problem:
Dynamically Add Images React Webpack
My current solution involves just requiring all of the images in the component - however, I'm wondering what the 'costs' of this are - just an increased bundle size? Attached is sample code to demonstrate what I mean:
import React from 'react';
import styles from './Background.css';
const pic0 = require('./imgs/0.jpg');
const pic1 = require('./imgs/1.jpg');
const pic2 = require('./imgs/2.jpg');
const pic3 = require('./imgs/3.jpg');
// etc etc etc
class Background extends React.Component {
constructor(props) {
super(props);
this.state = {
pic: {},
};
this.changeBackground = this.changeBackground.bind(this);
}
componentWillMount() {
this.setState({ pic: pic0 });
}
componentDidMount() {
// set timeout here
}
changeBackground() {
// change background via component state here
}
render() {
return (
<img className={styles.background} src={this.state.pic} />
);
}
}
export default Background;
Thoughts? Any help would be greatly appreciated.
It depends on which loader you configure webpack to use to load these images.
url-loader
When you use url loader, the contents of the image will be base64-encoded and embedded in your JavaScript bundle.
Thus the more images you require, the larger the bundle that the browser must download initially. The advantage is that all of the images are loaded in the browser and can display immediately.
file-loader
When you use file-loader, the image file is copied to your distribution folder (and usually renamed with a content hash) and the URL of the image is stored in your JavaScript bundle.
Requiring a lot of images has very little impact on the size of your JavaScript bundle. The app starts faster. The downside is that the browser will have to fetch the images from the server on demand as you display them.
Summary
url-loader: larger JS bundle, all images display instantly, harder to leverage image cache
file-loader: smaller JS bundle, images individually retrieved when displayed, makes better use of image cache.
Based on your problem statement (randomly cycle through large number of images), I'd start with file-loader.
The good news is your JS code is unchanged either way: you just treat the imported images as URLs either way. Its just a webpack reconfiguration if you change your mind.
Since you have a whole folder of images, I'd also recommend making use of webpack context to essentially require the whole folder in one line:
const imageReq = require.context("./imgs", false, /\.jpg$/);
setUrl(i) {
// "require" a specific image e.g. "./4.jpg"
const url = imageReq(`./${i}.jpg`);
this.setState({pic: url});
}
// ...