Build a working React contact form with no backend code. Create a form in formcarry, paste your unique endpoint URL into one of the 7 code examples below, and start receiving submissions by email in about 2 minutes.
With Fetch
With Axios
With Formcarry.js
Uploading Files
Drag & Drop Upload
With Toast
Field Validation
VS Code
Copy Code
1import{ useState }from"react";23exportdefaultfunctionFetch(){4const[email, setEmail]=useState("");5const[message, setMessage]=useState("");67const[submitted, setSubmitted]=useState(false);8const[error, setError]=useState("");910functionsubmit(e){11// This will prevent page refresh12 e.preventDefault();1314// replace this with your own unique endpoint URL15fetch("https://formcarry.com/s/XXXXXXX",{16method:"POST",17headers:{18"Content-Type":"application/json",19Accept:"application/json"20},21body:JSON.stringify({email: email,message: message })22})23.then((res)=> res.json())24.then((res)=>{25if(res.code===200){26setSubmitted(true);27}else{28setError(res.message);29}30})31.catch((error)=>setError(error));32}3334if(error){35return<p>{error}</p>;36}3738if(submitted){39return<p>We've received your message, thank you for contacting us!</p>;40}4142return(43<formonSubmit={submit}>44<labelhtmlFor="email">Email</label>45<input46id="email"47type="email"48value={email}49onChange={(e)=>setEmail(e.target.value)}50required51/>5253<labelhtmlFor="message">Message</label>54<textarea55id="message"56value={message}57onChange={(e)=>setMessage(e.target.value)}58/>5960<buttontype="submit">Send</button>61</form>62);63}
1import{ useState }from"react";2importaxiosfrom"axios";34exportdefaultfunctionApp(){5const[email, setEmail]=useState("");6const[message, setMessage]=useState("");78const[submitted, setSubmitted]=useState(false);9const[error, setError]=useState("");1011functionsubmit(e){12// This will prevent page refresh13 e.preventDefault();1415 axios
16.post(17// replace this with your own unique endpoint URL18"https://formcarry.com/s/XXXXXXX",19{20email: email,21message: message
22},23{24headers:{25Accept:"application/json"26}27}28)29.then((res)=>{30// success http code31if(res.data.code===200){32setSubmitted(true);33}else{34setError(res.data.message);35}36});37}3839if(error){40return<p>{error}</p>;41}4243if(submitted){44return<p>We've received your message, thank you for contacting us!</p>;45}4647return(48<formonSubmit={submit}>49<labelhtmlFor="email">Email</label>50<input51id="email"52type="email"53value={email}54onChange={(e)=>setEmail(e.target.value)}55required56/>5758<labelhtmlFor="message">Message</label>59<textarea60id="message"61value={message}62onChange={(e)=>setMessage(e.target.value)}63/>6465<buttontype="submit">Send</button>66</form>67);68}
1import{ useForm }from'@formcarry/react';23functionMyFormcarry(){4const{state: formcarryState,submit: formcarrySubmit}=useForm({5id:'Your-Form-ID-From-Formcarry'6});78if(formcarryState.submitted){9return<div>Thank you! We received your submission.</div>;10}1112return(13<formonSubmit={formcarrySubmit}>14<labelhtmlFor="name">Name</label>15<inputid="name"type="text"name="text"/>1617<labelhtmlFor="surname">Surname</label>18<inputid="surname"type="text"name="surname"/>1920<labelhtmlFor="email">Email</label>21<inputid="email"type="email"name="email"/>2223<labelhtmlFor="message">Message</label>24<textareaid="message"name="message"/>2526<buttontype="submit">Send</button>27</form>28);29}
1importReact,{ useRef }from"react"23constForm=()=>{4// create a Ref to access our form element5const formRef =useRef(null)67constsendFormData=async(event)=>{8// this will prevent your form to redirect to another page.9 event.preventDefault();1011if(!formRef.current){12console.log('something wrong with form ref')13return14}1516// get our form data17const formData =newFormData(formRef.current)1819// add some additional data if you want20// formData.append('language', window.navigator.language)2122fetch('https://formcarry.com/s/{Your-Unique-Endpoint}',{23method:'POST',24body: formData,25headers:{26// you don't have to set Content-Type27// otherwise it won't work due to boundary!28Accept:'application/json'29}30})31// convert response to json32.then(r=> r.json())33.then(res=>{34console.log(res);35});36}3738return(39// bind formRef to our form element40<formref={formRef}onSubmit={sendFormData}>41<labelhtmlFor="nameInput">Name</label>42<inputtype="text"id="nameInput"name="name"/>4344<labelhtmlFor="messageInput">Message</label>45<textareaid="messageInput"name="message"></textarea>4647<labelhtmlFor="pictureInput">Picture</label>48<inputtype="file"id="pictureInput"name="picture"/>4950<buttontype="submit">Submit</button>51</form>52)53}5455exportdefaultForm
1import{ useState, useCallback }from"react";2import{ useDropzone }from"react-dropzone";34exportdefaultfunctionDropzoneForm(){5const[email, setEmail]=useState("");6const[files, setFiles]=useState([]);7const[sending, setSending]=useState(false);8const[status, setStatus]=useState("");910const onDrop =useCallback((accepted)=>{11setStatus("");12setFiles((prev)=>[13...prev,14...accepted.map((f)=>Object.assign(f,{preview:URL.createObjectURL(f)}))15]);16},[]);1718const{ getRootProps, getInputProps, isDragActive }=19useDropzone({ onDrop,accept:{"image/*":[]}});2021functionremoveFile(index){22URL.revokeObjectURL(files[index].preview);23setFiles(files.filter((_, i)=> i !== index));24}2526asyncfunctionsubmit(e){27 e.preventDefault();28setSending(true);29setStatus("");3031// files require FormData instead of JSON32const formData =newFormData();33 formData.append("email", email);34 files.forEach((file, i)=> formData.append(`attachment_${i}`, file));3536try{37// replace with your formcarry endpoint URL38const response =awaitfetch("https://formcarry.com/s/YOUR-FORM-ID",{39method:"POST",40// skip Content-Type, the browser adds the multipart boundary41headers:{Accept:"application/json"},42body: formData
43});44const json =await response.json();4546if(response.ok&& json.code===200){47setFiles([]);48setStatus("Thanks! We received your message.");49}elseif(json.code===422){50setStatus(json.message);51}else{52setStatus("Something went wrong, please try again.");53}54}catch{55setStatus("Network error, please try again.");56}finally{57setSending(false);58}59}6061return(62<formonSubmit={submit}>63<labelhtmlFor="email">Email</label>64<inputid="email"type="email"value={email}required65onChange={(e)=>setEmail(e.target.value)}/>6667<div{...getRootProps({className:"dropzone"})}>68<input{...getInputProps()}/>69<p>{isDragActive ?"Drop the images here":"Drag images here, or click to select"}</p>70</div>7172{files.map((file, index)=>(73<divkey={file.preview}className="thumb">74{/* revoke the preview URL once the image has loaded */}75<imgsrc={file.preview}alt={file.name}width={80}76onLoad={()=>URL.revokeObjectURL(file.preview)}/>77<buttontype="button"onClick={()=>removeFile(index)}>Remove</button>78</div>79))}8081<buttontype="submit"disabled={sending}>82{sending ?"Sending...":"Send"}83</button>8485{status &&<p>{status}</p>}86</form>87);88}
1import{ useState }from"react";2importtoast,{Toaster}from"react-hot-toast";34exportdefaultfunctionToastForm(){5const[email, setEmail]=useState("");6const[message, setMessage]=useState("");7const[sending, setSending]=useState(false);89asyncfunctionsubmit(e){10 e.preventDefault();11setSending(true);1213// one toast morphs from loading to success or error via its id14const toastId = toast.loading("Sending your message...");1516try{17// replace with your formcarry endpoint URL18const response =awaitfetch("https://formcarry.com/s/YOUR-FORM-ID",{19method:"POST",20headers:{21"Content-Type":"application/json",22Accept:"application/json"23},24body:JSON.stringify({ email, message })25});26const json =await response.json();2728if(response.ok&& json.code===200){29 toast.success("Message sent, thank you!",{id: toastId });30setEmail("");31setMessage("");32}elseif(json.code===422){33 toast.error(json.message,{id: toastId });34}else{35 toast.error("Something went wrong, please try again.",{id: toastId });36}37}catch{38 toast.error("Network error, please try again.",{id: toastId });39}finally{40setSending(false);41}42}4344return(45<formonSubmit={submit}>46<labelhtmlFor="email">Email</label>47<input48id="email"49type="email"50value={email}51onChange={(e)=>setEmail(e.target.value)}52required53/>5455<labelhtmlFor="message">Message</label>56<textarea57id="message"58value={message}59onChange={(e)=>setMessage(e.target.value)}60/>6162<buttontype="submit"disabled={sending}>63{sending ?"Sending...":"Send"}64</button>6566<Toasterposition="bottom-center"/>67</form>68);69}
1import{ useState }from"react";23exportdefaultfunctionValidationForm(){4const[email, setEmail]=useState("");5const[message, setMessage]=useState("");6const[fieldErrors, setFieldErrors]=useState({});7const[error, setError]=useState("");8const[submitted, setSubmitted]=useState(false);9const[sending, setSending]=useState(false);1011// editing a field clears its stale validation error12functionclearFieldError(field){13setFieldErrors((prev)=>({...prev,[field]:undefined}));14}1516asyncfunctionsubmit(e){17 e.preventDefault();18setSending(true);19setFieldErrors({});20setError("");2122try{23// replace with your formcarry endpoint URL24const response =awaitfetch("https://formcarry.com/s/YOUR-FORM-ID",{25method:"POST",26headers:{"Content-Type":"application/json",Accept:"application/json"},27body:JSON.stringify({ email, message })28});29const json =await response.json();3031if(response.ok&& json.code===200){32setSubmitted(true);33}elseif(json.code===422){34// json.errors is keyed by field: { email: { message: "..." } }35const mapped ={};36for(const field in json.errors){37 mapped[field]= json.errors[field].message;38}39setFieldErrors(mapped);40}else{41setError(json.message||"Something went wrong, please try again.");42}43}catch{44setError("Network error, please try again.");45}finally{46setSending(false);47}48}4950if(submitted){51return<p>We've received your message, thank you for contacting us!</p>;52}5354return(55<formonSubmit={submit}>56<labelhtmlFor="email">Email</label>57<input58id="email"59type="email"60value={email}61onChange={(e)=>{62setEmail(e.target.value);63clearFieldError("email");64}}65className={fieldErrors.email?"field-error":""}66required67/>68{fieldErrors.email&&<pclassName="error-message">{fieldErrors.email}</p>}6970<labelhtmlFor="message">Message</label>71<textarea72id="message"73value={message}74onChange={(e)=>{75setMessage(e.target.value);76clearFieldError("message");77}}78className={fieldErrors.message?"field-error":""}79/>80{fieldErrors.message&&<pclassName="error-message">{fieldErrors.message}</p>}8182{error &&<pclassName="error-message">{error}</p>}8384<buttontype="submit"disabled={sending}>85{sending ?"Sending...":"Send"}86</button>87</form>88);89}
Step-by-step guide
How to build React contact form
What is React
React is a Javascript library for building user interfaces. It lets you create reusable components so that your code is easy to read and maintain.
Formcarry works with React, let us guide you through how can you use formcarry in react.
Before getting started
1- Create your form endpoint
Go to formcarry dashboard and create your first form, land on Setup section of your form then copy your unique endpoint URL.
💡 Each form has unique endpoint URL to it’s own, so make sure that you are using the right endpoint URL, otherwise you won’t get messages.
2- Create a HTML form
Check out our free contact form generator if you don’t have any written HTML form yet, by using this free tool you can customize and create a working contact form that works with formcarry without any further configuration
Formcarry setup with React using Fetch
Fetch is native API, so every app can use it without any installation, the steps are fairly simple.
VS Code
Copy Code
1import{ useState }from"react";23exportdefaultfunctionFetch(){4const[email, setEmail]=useState("");5const[message, setMessage]=useState("");67const[submitted, setSubmitted]=useState(false);8const[error, setError]=useState("");910functionsubmit(e){11// This will prevent page refresh12 e.preventDefault();1314// replace this with your own unique endpoint URL15fetch("https://formcarry.com/s/XXXXXXX",{16method:"POST",17headers:{18"Content-Type":"application/json",19Accept:"application/json"20},21body:JSON.stringify({email: email,message: message })22})23.then((res)=> res.json())24.then((res)=>{25if(res.code===200){26setSubmitted(true);27}else{28setError(res.message);29}30})31.catch((error)=>setError(error));32}3334if(error){35return<p>{error}</p>;36}3738if(submitted){39return<p>We've received your message, thank you for contacting us!</p>;40}4142return(43<formonSubmit={submit}>44<labelhtmlFor="email">Email</label>45<input46id="email"47type="email"48value={email}49onChange={(e)=>setEmail(e.target.value)}50required51/>5253<labelhtmlFor="message">Message</label>54<textarea55id="message"56value={message}57onChange={(e)=>setMessage(e.target.value)}58/>5960<buttontype="submit">Send</button>61</form>62);63}
Copied example to clipboard 👏
Formcarry setup with React using Axios
Below you will see the code that works using Axios, which is a library to perform HTTP requests, it’s fairly simple.
The code will show success or error message, and ready to work.
VS Code
Copy Code
1import{ useState }from"react";2importaxiosfrom"axios";34exportdefaultfunctionApp(){5const[email, setEmail]=useState("");6const[message, setMessage]=useState("");78const[submitted, setSubmitted]=useState(false);9const[error, setError]=useState("");1011functionsubmit(e){12// This will prevent page refresh13 e.preventDefault();1415 axios
16.post(17// replace this with your own unique endpoint URL18"https://formcarry.com/s/XXXXXXX",19{20email: email,21message: message
22},23{24headers:{25Accept:"application/json"26}27}28)29.then((res)=>{30// success http code31if(res.data.code===200){32setSubmitted(true);33}else{34setError(res.data.message);35}36});37}3839if(error){40return<p>{error}</p>;41}4243if(submitted){44return<p>We've received your message, thank you for contacting us!</p>;45}4647return(48<formonSubmit={submit}>49<labelhtmlFor="email">Email</label>50<input51id="email"52type="email"53value={email}54onChange={(e)=>setEmail(e.target.value)}55required56/>5758<labelhtmlFor="message">Message</label>59<textarea60id="message"61value={message}62onChange={(e)=>setMessage(e.target.value)}63/>6465<buttontype="submit">Send</button>66</form>67);68}
Copied example to clipboard 👏
Uploading files with React
Formcarry supports normal file uploads or base64 encoded strings. here’s an example of traditional way;
VS Code
Copy Code
1importReact,{ useRef }from"react"23constForm=()=>{4// create a Ref to access our form element5const formRef =useRef(null)67constsendFormData=async(event)=>{8// this will prevent your form to redirect to another page.9 event.preventDefault();1011if(!formRef.current){12console.log('something wrong with form ref')13return14}1516// get our form data17const formData =newFormData(formRef.current)1819// add some additional data if you want20// formData.append('language', window.navigator.language)2122fetch('https://formcarry.com/s/{Your-Unique-Endpoint}',{23method:'POST',24body: formData,25headers:{26// you don't have to set Content-Type27// otherwise it won't work due to boundary!28Accept:'application/json'29}30})31// convert response to json32.then(r=> r.json())33.then(res=>{34console.log(res);35});36}3738return(39// bind formRef to our form element40<formref={formRef}onSubmit={sendFormData}>41<labelhtmlFor="nameInput">Name</label>42<inputtype="text"id="nameInput"name="name"/>4344<labelhtmlFor="messageInput">Message</label>45<textareaid="messageInput"name="message"></textarea>4647<labelhtmlFor="pictureInput">Picture</label>48<inputtype="file"id="pictureInput"name="picture"/>4950<buttontype="submit">Submit</button>51</form>52)53}5455exportdefaultForm
Copied example to clipboard 👏
React contact form validation
Formcarry can validate submissions on the server: define rules in your form's Workflows and invalid submissions come back with a 422 response listing the failing fields. The Field Validation example above maps those errors under each input, so you get real validation without writing rules in your frontend.
For complex client-side needs (multi-step forms, dependent fields, instant feedback while typing), reach for a library like react-hook-form and keep posting to your formcarry endpoint. The two approaches combine well: client-side rules for speed, server-side rules as the source of truth.
How to send an email from a React contact form
You do not need Nodemailer, SMTP credentials, or an email server. Formcarry emails you every submission automatically, and can send an auto response to the person who filled in the form. Point the form at your endpoint URL and the email side is done.
React contact form FAQ
Do I need a backend for a React contact form?
No. Point your form's action or fetch request at a formcarry endpoint and submissions are stored and emailed to you, with no server code.
How do I send an email from a React contact form?
Post the form to your formcarry endpoint. Formcarry sends you an email for every submission and can auto respond to the sender, so you never touch SMTP.
How do I add file uploads to a React contact form?
Add an input with type="file" and submit the form as FormData (see the Uploading Files and Drag & Drop examples above). Formcarry stores the files with the submission.
How do I stop spam on a React contact form?
Formcarry filters spam automatically, no CAPTCHA setup required, and supports Google reCAPTCHA if your site attracts heavy bot traffic.
INSTALLATION
How to set up your React contact form
1
Sign up to formcarry to create your first form API endpoint then copy your endpoint
You can follow the steps like in the video above 👆 Save your endpoint to somewhere, you are going to use it in 3rd step.
2
Copy the example code.
If you are in a rush and want to quickly test it, consider using CodeSandbox to quickly test the code.
3
Paste the code and replace the form API endpoint that you got in 1st step.
Take the endpoint that you got from 1st step then replace the test endpoint inside the example code with your form endpoint.
4
Collect submissions ✨
Now you are ready to collect submissions from your React form.