Make an image preview in React js
Tags: react, javascript
Published: 2020-11-18 02:53:56

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! 😄).

![placeholder](https://dev-to-uploads.s3.amazonaws.com/i/mux6dlpqogj3frkz58ra.png)
 
```javascript
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.
```css
/* 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](https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications#Using_a_label_element_to_trigger_a_hidden_file_input_element).

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.
```react
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.
```react
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:
```react
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;
```
And this is what it would look like in the browser:
![Screenshot app](https://dev-to-uploads.s3.amazonaws.com/i/eg3dcdm7b0uk88aqy9ji.png)