83

Many examples of this on here but can't seem to find any for react. I have managed to convert the vanilla js to react but getting an error.

The answer looks simple enough so here I go in react:

getInitialState: function(){
  return{file: []}
},

_onChange: function(){
  // Assuming only image
  var file = this.refs.file.files[0];
  var reader = new FileReader();
  var url = reader.readAsDataURL(file);
  console.log(url) // Would see a path?
  // TODO: concat files for setState
},

render: function(){
 return(
  <div>
    <form>
      <input 
        ref="file" 
        type="file" 
        name="user[image]" 
        multiple="true"
        onChange={this._onChange}/>
     </form>
    {/* Only show first image, for now. */}
    <img src={this.state.file[0} />
  </div>
 )
};

Basically all answers I have seen show something like what I have. Any difference in React app?

Regarding answer:

enter image description here

Community
  • 1
  • 1
Sylar
  • 11,422
  • 25
  • 93
  • 166

16 Answers16

109

Extending on Cels' answer, and avoiding memory leaks with createObjectURL as @El Anonimo warns about, we could use useEffect to create the preview and clean up after the component unmounts like so

useEffect(() => {
   // create the preview
   const objectUrl = URL.createObjectURL(selectedFile)
   setPreview(objectUrl)

   // free memory when ever this component is unmounted
   return () => URL.revokeObjectURL(objectUrl)
}, [selectedFile])

The full code could look something like this

export const ImageUpload = () => {
    const [selectedFile, setSelectedFile] = useState()
    const [preview, setPreview] = useState()

    // create a preview as a side effect, whenever selected file is changed
    useEffect(() => {
        if (!selectedFile) {
            setPreview(undefined)
            return
        }

        const objectUrl = URL.createObjectURL(selectedFile)
        setPreview(objectUrl)

        // free memory when ever this component is unmounted
        return () => URL.revokeObjectURL(objectUrl)
    }, [selectedFile])

    const onSelectFile = e => {
        if (!e.target.files || e.target.files.length === 0) {
            setSelectedFile(undefined)
            return
        }

        // I've kept this example simple by using the first image instead of multiple
        setSelectedFile(e.target.files[0])
    }

    return (
        <div>
            <input type='file' onChange={onSelectFile} />
            {selectedFile &&  <img src={preview} /> }
        </div>
    )
}
Jay Wick
  • 12,325
  • 10
  • 54
  • 78
  • How to provide remove option ? – Raju Sah Jul 10 '20 at 01:53
  • 1
    Hey it's 2020! Nice! – Sylar Jul 13 '20 at 17:05
  • The key here is `URL.createObjectURL(selectedFile)` – M.Imran Mamda Oct 15 '20 at 20:32
  • 1
    Hey @Jay Wick is URL.createObjectURL deprecated? Came across this link: https://developers.google.com/web/updates/2018/10/chrome-71-deps-rems#remove_urlcreateobjecturl_from_mediastream – Reine_Ran_ Feb 20 '21 at 02:09
  • Still works in Nov, 2021. This saved me a lot of stress. Gracias amigo – Nzadibe Nov 20 '21 at 08:14
  • 3
    GG for Jay. June 2022 still works perfectly – Loudrous Jun 18 '22 at 11:58
  • @Reine_Ran_ it looks like it might be only for media streams as per notes here https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/srcObject#:~:text=from%20Blob).-,Note,-%3A%20As%20of and here https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL#using_object_urls_for_media_streams – Jay Wick Oct 09 '22 at 23:41
  • is there a way to pass preview to a different react component? – Yehuda Nov 22 '22 at 00:07
  • 1
    @Yehuda does it not work if you invoke a callback prop with the objectUrl? Something like `props.onPreviewChange(objectUrl)` instead of the `setPreview(objectUrl)` call? Of course as with react, it will go to parent component, and you will have to manage state between peer components in another way – Jay Wick Nov 27 '22 at 11:02
58

No difference, just read your image when the load event finishes. After the load end event handler just set your state:

getInitialState: function(){
  return{file: []}
}

_onChange: function(){
  // Assuming only image
  var file = this.refs.file.files[0];
  var reader = new FileReader();
  var url = reader.readAsDataURL(file);

   reader.onloadend = function (e) {
      this.setState({
          imgSrc: [reader.result];
      })
    }.bind(this);
  console.log(url) // Would see a path?
  // TODO: concat files
},

render: function(){
 return(
  <div>
    <form>
      <input 
        ref="file" 
        type="file" 
        name="user[image]" 
        multiple="true"
        onChange={this_onChange}/>
     </form>
    {/* Only show first image, for now. */}
    <img src={this.state.imgSrc} />
  </div>
 )
}
Leandro Caniglia
  • 14,495
  • 4
  • 29
  • 51
Piyush.kapoor
  • 6,715
  • 1
  • 21
  • 21
  • Thanks. `result` is empty in the hash but not empty when outside. Make sense? `reader` shows the hash but `result` is empty. See image in post. – Sylar Jun 27 '16 at 09:37
  • in the function it might be empty because we have bind this the loadend function , debug the render function , and check the state it should be updated – Piyush.kapoor Jun 27 '16 at 09:46
  • 6
    I've spot the bug in your code. Change `reader.result;` to `[reader.result]` Thanks!! – Sylar Jun 27 '16 at 09:54
  • Update your answer so i wrap this question up. Thanks. – Sylar Jun 27 '16 at 10:10
  • Refs are depreciated, what to do now. – Jovylle Oct 25 '20 at 17:11
  • Refs are deprecated, You have to access [current] object now before accessing files example : this.textInput.current.focusTextInput() – Mohammed Metwally Mar 21 '21 at 18:50
  • 1
    This answer is old, but I wanted to share what I came across, No need to do all the tedios work of initializing a fileReader & stuff, You can `const file = e.target.files[0]; if (!file) return; // else it will throw an parse error const url = URL.createObjectURL(file);` `url` can be bound to `` like this. Hope someone finds this helpful. – Hasintha Abeykoon Jun 26 '21 at 10:35
20

Please this worked perfectly for me

state = {
  img: logo
}

handleChangeImage = e => {
  this.setState({[e.target.name]: URL.createObjectURL(e.target.files[0])})
}

<input type="file" id="img" name="img" accept="image/*" className="w-100" onChange={this.handleChangeImage}/>

<img src={this.state.img} alt="img"/>

Check the link for more details https://medium.com/@650egor/react-30-day-challenge-day-2-image-upload-preview-2d534f8eaaa

Solomon Darku
  • 357
  • 2
  • 6
  • Wow, I was stuck with `FileReader()` about 2 hours for previewing multiple photos, actually `URL.createObjectURL` is super easy and support in all browsers. – ronaldtgi Jul 19 '21 at 04:19
19

Here is a simple and working solution which displays all selected images

getInitialState: function(){
  return{file: []}
}

_onChange: (event)=>{
    this.setState({
        imgs: event.target.files
    })
},

render: function(){
 return(
  <div>
    <form>
      <input 
        ref="file" 
        type="file" 
        name="user[image]" 
        multiple="true"
        onChange={this._onChange}/>
    </form>

    {/* Display all selected images. */}        
    {this.state.imgs && [...this.state.imgs].map((file)=>(
       <img src={URL.createObjectURL(file)} />
    ))}

  </div>
 )
}
Cels
  • 1,212
  • 18
  • 25
  • 8
    Please keep in mind each time you `createObjectURL(file)` the resulting URL stays in the memory. To prevent a memory leak call `URL.revokeObjectURL()` to remove the old image URL from the memory. Found on https://medium.com/@scyrizales/dont-forget-that-if-you-are-going-to-use-this-to-show-images-when-you-replace-the-image-with-a688b397725b – El Anonimo Jan 07 '19 at 17:23
7

Here is a good example: https://www.kindacode.com/article/react-show-image-preview-before-uploading/

const App = () => {
  const [selectedImage, setSelectedImage] = useState();

  const imageChange = (e) => {
    if (e.target.files && e.target.files.length > 0) {
      setSelectedImage(e.target.files[0]);
    }
  };

  const removeSelectedImage = () => {
    setSelectedImage();
  };

  return (
    <>
      <div>
        <input
          accept="image/*"
          type="file"
          onChange={imageChange}
        />

        {selectedImage && (
          <div>
            <img
              src={URL.createObjectURL(selectedImage)}
              alt="Thumb"
            />
            <button onClick={removeSelectedImage}>
              Remove This Image
            </button>
          </div>
        )}
      </div>
    </>
  );
};
Vladimir Pankov
  • 377
  • 3
  • 8
2

I had a crazy time trying to get this to work with multiple files, so if anyone else is having this problem, here you go:

const onFileChange = e => {
e.preventDefault();
const filesList = e.target.files;

if (filesList.length === 0) return;
//Spread array to current state preview URLs
let arr = [...data.imagePreviewUrls];
for (let i = 0; i < filesList.length; i++) {
  const file = filesList[i];

  const reader = new FileReader();
  reader.onload = upload => {
    //push new image to the end of arr
    arr.push(upload.target.result);
    //Set state to arr
    setData({
      ...data,
      imagePreviewUrls: arr
    });
  };
  reader.readAsDataURL(file);
}
};

Essentially the for loop resolves before even the first onload event fires, resetting the state every time it iterates. To resolve this just make an array outside of the for loop and push the new item to it onload. This will handle multiple files even if the user closes the file dialog and later chooses to upload more, as long as state hasn't been reset.

Brandon Miller
  • 1,534
  • 1
  • 11
  • 16
2

Try This multiple Upload and Preview:

import React,{useState} from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import IconButton from '@material-ui/core/IconButton';
import PhotoCamera from '@material-ui/icons/PhotoCamera';
 

const useStyles = makeStyles((theme) => ({
  root: {
    '& > *': {
      margin: theme.spacing(1),
    },
  },
  input: {
    display: 'none',
  },
}));

export default function UploadButtons() {
  const classes = useStyles();
  const [files,setFile]=useState([]);
  const handlerFile=(e)=>{    
    console.log(e.target.files);
    
    let allfiles=[]
   for(let i=0;i<e.target.files.length;i++){
    allfiles.push(e.target.files[i]);
   }
    if(allfiles.length>0){
      setFile(allfiles);  
    }
  };
  return (
    <div className={classes.root}>
        {files.map((file, key) => {
                            return (
                                <div key={key} className="Row">
                                    <span className="Filename">
                                        {file.name}
                                        <img src={URL.createObjectURL(file)}  alt={file.name} />
                                    </span>
                                </div>
                            )
                        })}
      <input
        accept="image/*"
        className={classes.input}
        id="contained-button-file"
        multiple
        type="file"
        onChange={handlerFile}
      />
      <label htmlFor="contained-button-file">
        <Button variant="contained" color="primary" component="span">
          Upload
        </Button>
      </label> 
      
  );
}
saman samadi
  • 436
  • 2
  • 8
  • 19
1

There is my solution. Very simple and easy to use.

JSX

<form onSubmit={this.onSubmit} >      
      <input
         ref="uploadImg"
         type="file"
         name="selectedFile"
         onChange={this._onChange}
        />
</form>

<img src={this.state.imageUrl} />

And function

  _onChange = (e) => {

    const file    = this.refs.uploadImg.files[0]
    const reader  = new FileReader();

    reader.onloadend = () => {
        this.setState({
            imageUrl: reader.result
        })
    }
    if (file) {
        reader.readAsDataURL(file);
        this.setState({
            imageUrl :reader.result
        })
    } 
    else {
        this.setState({
            imageUrl: ""
        })
    }
}

And of course You need to have state like imageUrl which is our src in jsx.

Skylin R
  • 2,111
  • 1
  • 20
  • 23
  • use [ObjectURL](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL) instead of base64... – Endless Mar 13 '18 at 20:50
1

You can use base64 and use that result in the img src. Here I am using typescript, but you can use the same logic with pure JS

(file: Blob):Promise< string | ArrayBuffer | null> => new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => resolve(reader.result);
    reader.onerror = error => reject(error);
}),
Mohammed
  • 2,215
  • 1
  • 9
  • 8
1

This is based on @Mohammed's answer with an example of how to use it

const loadImage = (file: Blob) =>
  new Promise<string>((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () =>
      typeof reader.result === "string" ? resolve(reader.result) : reject(reader.result);
    reader.onerror = error => reject(error);
  });

export const FilePreview: React.FC<{ file: File }> = ({ file }) => {
  const [imageUrl, setImageUrl] = React.useState<string | undefined>(undefined);
  React.useEffect(() => {
    loadImage(file).then(setImageUrl);
  }, [file]);
  return (
    <div>
      {imageUrl && <img src={imageUrl} alt={file.name} />}
      <div>{file.name}</div>
    </div>
  );
};
JLarky
  • 9,833
  • 5
  • 36
  • 37
1
 1. image with preview and close button

import React,{useState} from "react";

export default function App() {
  const  [preview,setPreview] = useState([]);

  const maxLength = 3;



  const onSelectFile=(e)=>{
      for (var i=0;i < e.target.files.length;i++){
        arrayofFiles.push(e.target.files[i]);
        
    }
    
  let images = [];
  arrayofFiles.map((e)=>{
  const ImageUrl =  URL.createObjectURL(e);
  images.push(ImageUrl)
      
  })
  setPreview(images)
  }



  const removeImageFromArray=(e)=>{
    const index = e.target.id;
    let newPreview = [...preview];

    newPreview.splice(index,1);
    setPreview(newPreview);
  }
  return (
    <div className="App">
      <h1>Image view and close</h1>
      <input type="file" name="file" multiple onChange={onSelectFile}/>
    {preview.map((img,index)=>
      (
        <div key={index}>
         <img src={img} id={index} alt="pic1" width="250" height="250" />
           <button
              id={index}
              key={index}
              onClick={(e) => {
                removeImageFromArray(e);
              }}
            >
              Close
            </button>

        </div>
      )  
    )}
     </div>
  );
}
1

Try This

import React,{useState} from 'react';

export default function App() {
  const [photo123, setphoto123] = useState(null);
  const handleInputChange = (event) => {
    setphoto123(URL.createObjectURL(event.target.files[0]));
  }

  return (
    <div style={{margin:50}}>
      <input type="file" className="form-control"
          onChange={handleInputChange} />
      <img  src={photo123} alt="Image_Photo" />
    </div>
  );
}

Merrin K
  • 1,602
  • 1
  • 16
  • 27
0

You can use react-use-file-uploader in it you set the type of files you need, after selecting you will receive an array with objects inside which there is a preview of the file and much more, the metadata of the audio file also enters the library and you can get information about the album including the cover image, a freeze frame is created for the video file for preview.

  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Apr 15 '23 at 10:53
-2

-- Constructor

constructor(props) {
  super(props);
  this.state = {
     imgRef: ""
  };
}

-- Function

filePreview(e) {
    const file = e.target.files[0];
    this.setState({
        imgRef: URL.createObjectURL(file)
    });
}

-- Html

<input
   type="file"
   name="photo"
   onChange={this.fileSelectHandler.bind(this)}
/>
<img src={this.state.imgRef} />
Jonata
  • 1
-2
import React from "react";
import { useRef } from "react";
import { useState } from "react";

const ControlledForm = () => {
  let [photo, setPhoto] = useState("");
  let imgRef = useRef();
  let handleSubmit = e => {
    e.preventDefault();
    console.log({ photo });
  };

  return (
    <div id="myFormDiv">
      <form onSubmit={handleSubmit}>
        <input
          accept="image/*"
          type="file"
          onChange={e => {
            console.log(e.target.files);
            setPhoto(e.target.value);
            var src = URL.createObjectURL(e.target.files[0]);
            imgRef.current.src = src;
          }}
        />
        <img style={{ height: "200px", width: "200px" }} ref={imgRef} />
        <button>SUBMIT</button>
      </form>
    </div>
  );
};

export default ControlledForm;
-3

URL.createObjectURL(file) short and fine solution