Example of Netlify Forms with file upload using React, Hooks, Fetch, react-dropzone

Hi! I just wanted to share a simple example of doing a form submission using Netlify Forms with a file upload in React. I’m using react-dropzone, but you can use a regular file input in its place. State is managed using useState hooks and submission is handled with fetch.

Demo: https://demo-react-netlify-form-file.netlify.com/
Repo: https://github.com/futuregerald/react-netlify-form-file

Let me know if you have any questions!


this is awesome <3 :muscle:

1 Like

I could probably google this, but how does Netlify handle file uploads for form submissions?

Yep, I was lazy:

Netlify Forms can receive files uploaded via form submissions. To do this, add an input with type="file" to any form. When a form is submitted, a link to each uploaded file will be included in the form submission details. These are viewable in the Netlify app, in email notifications, and via our API.

So nice and easy. :slight_smile:

I checked out your repo and I’m still a little confused, you didn’t place a hidden html form in your index.html? Does netlify find the form and it all appears in your deploy dashboard under forms?

@richdacuban, if you clone and build this repo, it creates the HTML version of the form in public/form.html.

I included my clone/build process below in hopes that it clarifies this.

$ git clone https://github.com/futuregerald/react-netlify-form-file
Cloning into 'react-netlify-form-file'...
remote: Enumerating objects: 20, done.
remote: Counting objects: 100% (20/20), done.
remote: Compressing objects: 100% (20/20), done.
remote: Total 20 (delta 1), reused 16 (delta 0), pack-reused 0
Unpacking objects: 100% (20/20), done.
$ cd react-netlify-form-file/
$ yarn install
yarn install v1.17.3
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
warning "react-scripts > @typescript-eslint/eslint-plugin@1.6.0" has unmet peer dependency "typescript@*".
warning "react-scripts > @typescript-eslint/parser@1.6.0" has unmet peer dependency "typescript@*".
warning "react-scripts > @typescript-eslint/eslint-plugin > @typescript-eslint/typescript-estree@1.6.0" has unmet peer dependency "typescript@*".
warning "react-scripts > @typescript-eslint/eslint-plugin > tsutils@3.17.1" has unmet peer dependency "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta".
[4/4] 🔨  Building fresh packages...
✨  Done in 10.04s.
$ yarn build
yarn run v1.17.3
$ react-scripts build
Creating an optimized production build...
Compiled successfully.

File sizes after gzip:

  45.09 KB  build/static/js/2.8382b1d1.chunk.js
  1.02 KB   build/static/js/main.0acd8c8f.chunk.js
  762 B     build/static/js/runtime~main.a8a9905a.js

The project was built assuming it is hosted at the server root.
You can control this with the homepage field in your package.json.
For example, add this to build it for GitHub Pages:

  "homepage" : "http://myname.github.io/myapp",

The build folder is ready to be deployed.
You may serve it with a static server:

  yarn global add serve
  serve -s build

Find out more about deployment here:


✨  Done in 8.76s.
$ cd public/
$ grep -irl '<form ' .

With the contents of the file form.html being:

<!DOCTYPE html>
<html lang="en">

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <form name="contact" netlify method='post' enctype='multipart/form-data' action="/thank-you/">
        <input type="text" name="name" />
        <input type="email" name="email" />
        <textarea name="message"></textarea>
        <input type="file" id="file" name="file" multiple>


It is this HTML version of the form which is required for Netlify to create the endpoints to receive the incoming form submissions. An HTML version is always required - even if the live site doesn’t use it to submit forms.

Netlify processes the HTML version to create the endpoints to receive form submissions. This is why it must exist in the deployed site. The HTML form is the template Netlify uses for form submissions. If Netlify cannot find a ready HTML version of the form, no endpoint will be created to receive it.

Forms can be submitted using a different form (like an HTML form created at browse time by client side javascript) and/or by javascript itself using AJAX/XHR - as long as the HTML form existed and was processed at deploy time to create the form endpoint. (You can even make redirect rules which make the HTML form unreachable - it is only required for endpoint creation but it must exist for this reason.)

If there are other questions about this, we are happy to answer.

1 Like

This is a GREAT answer! Thank you!

Incidentally, I did get my own project to work (I was posting here while researching creating a dynamic React site and using form submissions on netlify - the answer: same as above; a hidden input form with matching inputs/names in the index.html) though I literally built my own by hand statically; I wasn’t aware of this package.

The only issue I’m having now is uploading files via the form. I created my own drag and drop uploading component (so it isn’t an actual in the , rather a component that appends file objects to an array in local component state) AND I included a matching <input type=‘file’ name='uploads> in the hidden html form I built BUT the field for uploads in the form dashboard is blank? The other fields of the form populate fine from the form, INCLUDING an array of objects I accidentally uploaded, they just appeared as string objects. Is it not possible to upload multiple files to forms?

Thanks for any guidance you can provide!

Hi @richdacuban, in the repo above there’s an example of doing file upload with react. while I use the react-dropzone package, the submission should be basically the same since you have to provide the file info in the same way.