Image Upload in a React/Rails App

I recently made an application using React for the front-end, and Rails for the back-end. The app is a social media application where a user can make posts with word content and a picture. At first, the only way a user could add pictures was with an image URL from another website. But this wasn’t very user friendly, so I looked into ways to upload pictures from a user’s computer to the app. I’m going to talk through my process in this blog post, with the assumption the reader knows how React and Rails work.

First, I found a way to prompt the user to select a photo from their files to add to their post. In my React Component for adding a post I added an input with type file to my form, like this:

<form onSubmit={submitPost}>
. . .
<div>
<label>Input Image</label>
<input type="file" onChange={handleFileChange} />
</div>
. . .
</form>

A file type input will look like a button that says “choose file” and it will bring up a window where you can choose a file from your computer. In it’s most basic form it looks like this when clicked (side note: using CSS to style an input with type file is very difficult, and I ultimately gave up on trying to change it’s appearance and left it as is in my app):

file type input with file selection window open
file type input with file selection window open

Now, the purpose of the handleFileChange method I used in the input element is to save the selected file in my component’s state. I did this by using React Hooks to have a file variable and setFile method:

const [file, setFile] = useState(null). . .const handleFileChange = event => {
setFile(event.target.files[0])
}

And finally, when the user would submit the finished post, in the submitPost method I had to find a way to send the file to my Rails back-end. I found that the way to do this is by making a FormData JavaScript object like so (side note: I would usually have a headers part of the fetch with “Content-Type”: “application/json” but I had to omit it because the body type was no longer JSON):

const submitPost = event => {
event.preventDefault()
const formData = new FormData()
formData.append("image", file)
formData.append("content", content)
fetch(`${BASE_URL}/posts`, {
method: "POST",
body: formData
})
.then(resp => resp.json())
.then( . . . )
}

But after I got to this point, I realized I had to figure out how to update the Rails part of my app to be able to handle the FormData object I was sending to it. The way that I found to do this was by using Cloudinary.

First, I had to make an account with Cloudinary and add the cloudinary gem to my rails project’s Gemfile. Then I had to configure my project with my account-specific configuration credentials in a cloudinary.yml file in the config directory. This is an example of what the file looks like:

production:
cloud_name: "sample"
api_key: "874837483274837"
api_secret: "a676b67565c6767a6767d6767f676fe1"
secure: true
cdn_subdomain: true

Finally, in my posts_controller.rb file, in the create method I use the upload method from the cloudinary gem to upload the file I got from the user in my React front-end to my Cloudinary account. The response I get back from Cloudinary is a hash with a variety of different keys, but the only one I care about for my purposes is the url. I then save this URL as the image URL for the post like so:

def create
post = Post.create(content: params[:content])
image = Cloudinary::Uploader.upload(params[:image])
post.update(post_img: image["url"])
. . .
end

And this was everything I needed to make my app able to handle user image uploads. I can’t promise everything I did was the best way to go about solving my problem, but it definitely resulted in a more professional and accessible application.

Written by

Software Engineer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store