It is a cool UI interaction when apps show a preview of the added image to the user before they upload it to the server. This post explains how to achieve that using React js.
We're going to use a functional component called ImgPrev.js, the placeholder image is needed to be (you guessed it) a placeholder for the img tag, we also need to import the CSS style file to complete the magic.
This is the photo I used as a placeholder (If you can't see it, it's because the photo is white, but trust me it's there! 😄).
import React, { useState } from 'react';
import placeholder from '../images/placeholder.png';
import './style.css';
const ImgPrev = () => {
return(
<form></form>
);
}
export default ImgPrev;
Now let's talk a little bit about that stylesheet.
/* This is used to reset the default styles
applied to every element in this app */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.form__title {
margin: 32px 0;
text-align: center;
color: #002952;
}
.form__img-input-container {
position: relative;
width: 300px;
height: 300px;
margin: auto;
}
/* this class is used to hide the file input */
.visually-hidden {
position: absolute;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
}
/* Adding the focus and focus-within styles for accessibility */
input.visually-hidden:focus + label,
input.visually-hidden:focus-within + label {
outline: 8px solid rgba(86, 206, 239, .4);
}
/* The file input is a labeled control, and since we hid it,
we're going to use its associated label to trigger it */
.form-img__file-label {
position: absolute;
top: 0;
left: 0;
display: block;
width: 300px;
height: 300px;
border-radius: 50%;
cursor: pointer;
background-color: rgba(245, 245, 231, .3);
}
.form-img__file-label:hover {
background-color: rgba(245, 245, 231, .5);
}
.form-img__file-label > svg {
position: absolute;
top: 50%;
left: 50%;
opacity: .7;
transform: translate(-50%, -50%);
}
.form-img__file-label:hover > svg {
opacity: 1;
}
/* The image element is going to be positioned under the
label element, since the label is a see through, we're going
to be able to see the preview of the image. */
.form-img__img-preview {
display: block;
width: 300px;
height: 300px;
object-fit: contain;
border-radius: 50%;
border: 16px rgb(80, 199, 235) solid;
}
To hide the file input I used the style rules mentioned in this MDN page.
Our component is going to return some JSX code that includes the file input wich will accept only these types of files: png, jpg and jpeg, as soon as the user clicks on the label and chooses (or cancels) an image, the handleImg method will fire, and the img tag will display (or not) the chosen image by the user.
return (
<form encType="multipart/form-data">
<h1 className="form__title">Image Preview in Reactjs</h1>
<div className="form__img-input-container">
<input
type="file"
accept=".png, .jpg, .jpeg"
id="photo"
className="visually-hidden"
onChange={handleImg}
/>
<label htmlFor="photo" className="form-img__file-label">
<svg width="150" height="150" viewBox="0 0 24 24" fill="none" stroke="#56ceef" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round">
<path d="M5.52 19c.64-2.2 1.84-3 3.22-3h6.52c1.38 0 2.58.8 3.22 3" />
<circle cx="12" cy="10" r="3" />
<circle cx="12" cy="12" r="10" />
</svg>
</label>
<img src={src} alt={alt} className="form-img__img-preview"/>
</div>
</form>
);
To make sure that our image is being displayed we need to use a state to store it and this will ensure that in every state update the component will be re-rendered and every time that happens, the component will preview the chosen image.
Our state stores the src and the alt attributes of the image tag and has the placeholder and 'Upload an Image' as the default values.
const [{alt, src}, setImg] = useState({
src: placeholder,
alt: 'Upload an Image'
});
In the handleImg method, we need to check first if the user chose an image or not, if we don't, this error will appear TypeError: Failed to execute 'createObjectURL' on 'URL': Overload resolution failed..
inside the if statement, the state is being updated by creating a URL that points to the image we're going to preview, we're also updating the alt attribute using the image's name.
Finally, this is the whole component:
import React, { useState } from 'react';
import placeholder from '../images/placeholder.png';
import './style.css';
const ImgPrev = () => {
const [{alt, src}, setImg] = useState({
src: placeholder,
alt: 'Upload an Image'
});
const handleImg = (e) => {
if(e.target.files[0]) {
setImg({
src: URL.createObjectURL(e.target.files[0]),
alt: e.target.files[0].name
});
}
}
return (
<form encType="multipart/form-data">
<h1 className="form__title">Image Preview in Reactjs</h1>
<div className="form__img-input-container">
<input
type="file"
accept=".png, .jpg, .jpeg"
id="photo"
className="visually-hidden"
onChange={handleImg}
/>
<label htmlFor="photo" className="form-img__file-label">
<svg width="150" height="150" viewBox="0 0 24 24" fill="none" stroke="#56ceef" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round">
<path d="M5.52 19c.64-2.2 1.84-3 3.22-3h6.52c1.38 0 2.58.8 3.22 3" />
<circle cx="12" cy="10" r="3" />
<circle cx="12" cy="12" r="10" />
</svg>
</label>
<img src={src} alt={alt} className="form-img__img-preview"/>
</div>
</form>
);
}
export default ImgPrev;
Top comments (2)
Great!!
This is great, I really liked the UI. But any idea how I can add multiple images??
Thanks