A contact form is an essential component
of any website, allowing visitors to reach
out to you with their queries or feedback.
In this tutorial, I will show you how to create a contact form with Formgrid, React, and Styled Components.
We will start by creating the basic form structure and styling it with Styled Components.
Then we will integrate the Formgrid API to handle form submissions.
Formgrid is an open-source form backend and no-code form builder that makes it simple to handle form submissions without any server-side coding. By the end of this tutorial, you will have a working contact form that sends email notifications to your inbox whenever someone submits the form.
Table of Contents
- Prerequisites
- Setting Up the React Project
- Creating the Form Page Layout
- Creating The Details Bar Component
- Creating The Input Side and Input Fields
- Declaring The State Variables and Input Handler Functions
- Creating a Formgrid Account
- Handling Form Submission
- Creating The Success Page
- Redirecting Users to The Success Page
- Conclusion
Prerequisites
- Basic knowledge of React and JavaScript
- A free Formgrid account
Setting Up The React Project
First, create a new React project using create-react-app. 'Open your terminal, navigate to your desired directory, and
run the following command:
npx create-react-app formgrid-react-contact-form
This will create a new React project named formgrid-react-contact-form.
Once the project is created, navigate to
the project root directory by running:
cd formgrid-react-contact-form
We will be using styled-components for
styling our components and react-reveal
for fade-in animation.
Run the following command to install
the dependencies:
npm i styled-components react-reveal
Creating The Form Page Layout
Now that we have our project set up let us create the page layout. In your project's src folder create a new folder called pages. Navigate to the pages folder and create a new file called
FormPage.js. Add the following code
to FormPage.js:
//FormPage.js
import React from 'react';
import styled from 'styled-components';
const PageWrapper = styled.div`
display: flex;
flex-direction: column;
min-height: 100vh;
align-items: center;
background-color: whitesmoke;
padding-bottom: 50px;
`;
const PageHeadingWrapper = styled.div`
display: flex;
flex-direction: column;
margin-top: 40px;
`;
const FormContainer = styled.div`
width: 70%;
background-color: #fff;
padding: 5px;
border-radius: 5px;
height: 70vh;
@media (max-width: 768px) {
width: 90%;
}
`;
const TextOne = styled.b`
font-size: 30px;
color: rgb(4, 4, 59);
text-align: center;
`;
const TextTwo = styled.p`
color: rgb(4, 4, 34);
font-size: 15px;
text-align: center;
`;
const FormPage = () => {
return (
<PageWrapper>
<PageHeadingWrapper>
<TextOne>Contact US</TextOne>
<TextTwo>
Any Question or remarks?
Just write us a message
</TextTwo>
</PageHeadingWrapper>
<FormContainer></FormContainer>
</PageWrapper>
);
};
export default FormPage;
Creating The Details Bar Component
In your project's src folder create a
new folder called components.
Navigate to the components folder and create a new file called DetailsBar.js. Add the
following code to DetailsBar.js:
//DetailsBar.js
import React from 'react';
import styled from 'styled-components';
import * as Icon from 'react-feather';
const DetailsBarWrapper = styled.div`
background-color: rgb(8, 8, 63);
border-radius: 7px;
position: relative;
padding: 30px;
display: flex;
flex-direction: column;
align-items: center;
height: auto;
padding-bottom: 100px;
@media (max-width: 768px) {
padding-bottom: 80px;
grid-row: 2;
}
`;
const TextWrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
`;
const TextOne = styled.p`
text-align: center;
color: #fff;
font-size: 18px;
font-weight: bold;
`;
const TextTwo = styled.p`
text-align: center;
color: #fff;
font-size: 12px;
line-height: 18px;
`;
const BigCircle = styled.div`
height: 50px;
margin-top: 30px;
width: 50px;
background-color: rgb(100, 21, 173);
border-radius: 100%;
z-index: 22;
margin-left: 10px;
`;
const SmallCircle = styled.div`
position: absolute;
margin-left: 10px;
background-color: rgb(252, 113, 137);
border-radius: 100%;
height: 30px;
width: 30px;
`;
const ContactsWrapper = styled.a`
display: flex;
width: 200px;
height: 10px;
margin-top: 50px;
cursor: pointer;
text-decoration: none;
`;
const ContactText = styled.div`
color: #fff;
font-size: 15px;
margin-left: 10px;
`;
const SocialsWrapper = styled.div`
display: flex;
justify-content: center;
height: 10px;
bottom: 30px;
position: absolute;
cursor: pointer;
`;
const SocialIconWrapper = styled.a`
width: 35px;
height: 35px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
&:hover {
background-color: rgb(252, 113, 137);
}
`;
const DetailsBar = () => {
return (
<DetailsBarWrapper>
<TextWrapper>
<TextOne>Contact Information</TextOne>
<TextTwo>
Fill up the form and our team
will get back to you within
24 hours
</TextTwo>
</TextWrapper>
<div>
<ContactsWrapper href="tel:+233543201893">
<Icon.Phone
size={15}
color="rgb(252, 113, 137)"
/>
<ContactText>
+233543201893
</ContactText>
</ContactsWrapper>
<ContactsWrapper
href="mailto:aljay3334@gmail.com"
>
<Icon.Mail
size={15}
color="rgb(252, 113, 137)"
/>
<ContactText>
aljay3334@gmail.com
</ContactText>
</ContactsWrapper>
</div>
<div>
<BigCircle></BigCircle>
<SmallCircle></SmallCircle>
</div>
<SocialsWrapper>
<SocialIconWrapper
href="https://www.facebook.com"
>
<Icon.Facebook color="#fff" size={20} />
</SocialIconWrapper>
<SocialIconWrapper
href="https://www.instagram.com"
>
<Icon.Instagram color="#fff" size={20} />
</SocialIconWrapper>
<SocialIconWrapper
href="https://www.linkedin.com"
>
<Icon.Linkedin color="#fff" size={20} />
</SocialIconWrapper>
</SocialsWrapper>
</DetailsBarWrapper>
);
};
export default DetailsBar;
In FormPage.js import the DetailsBar
component into it as shown in the
code below:
//FormPage.js
import React from 'react';
import styled from 'styled-components';
import DetailsBar from '../components/DetailsBar';
const FormPage = () => {
return (
<PageWrapper>
<PageHeadingWrapper>
<TextOne>Contact US</TextOne>
<TextTwo>
Any Question or remarks?
Just write us a message
</TextTwo>
</PageHeadingWrapper>
<FormContainer>
<DetailsBar />
</FormContainer>
</PageWrapper>
);
};
export default FormPage;
Creating The Input Side
Let us create the input side and add the input fields.
We will style the input fields with styled components and make them responsive using CSS media queries.
In the components folder create a
new file called InputSide.js and
add the following code:
//InputSide.js
import React from 'react';
import styled from 'styled-components';
const InputSideWrapper = styled.form`
height: auto;
padding-bottom: 100px;
position: relative;
padding: 10px 10px 100px 10px;
`;
const InputWrapper = styled.div`
border: 2px solid transparent;
width: 90%;
padding-left: 10px;
display: flex;
flex-direction: column;
`;
const Input = styled.input`
color: #333;
width: 100%;
font-size: 15px;
padding: 8px;
border-bottom: 1px solid rgb(100, 21, 173);
border-left: 1px solid transparent;
border-right: 1px solid transparent;
border-top: 1px solid transparent;
outline: 0px transparent !important;
`;
const MessageInput = styled.textarea`
width: 100%;
color: #333;
font-size: 15px;
padding: 10px;
border-bottom: 1px solid rgb(100, 21, 173);
border-left: 1px solid transparent;
border-right: 1px solid transparent;
border-top: 1px solid transparent;
outline: 0px transparent !important;
`;
const SubMitButton = styled.input`
position: absolute;
bottom: 20px;
right: 20px;
padding: 10px;
background-color: rgb(8, 8, 63);
color: #fff;
border: none;
border-radius: 5px;
padding: 12px 25px 12px 24px;
cursor: pointer;
`;
const InputSide = () => {
return (
<InputSideWrapper>
<InputWrapper>
<p>Name</p>
<Input
type="text"
placeholder="Allen Jones"
/>
</InputWrapper>
<InputWrapper>
<p>Email</p>
<Input
type="email"
placeholder="aljay126@gmail.com"
/>
</InputWrapper>
<InputWrapper>
<p>Phone</p>
<Input
type="number"
placeholder="+233546227893"
/>
</InputWrapper>
<InputWrapper>
<p>Message</p>
<MessageInput
placeholder="Write your message"
/>
</InputWrapper>
<SubMitButton
type="submit"
value="Send Message"
/>
</InputSideWrapper>
);
};
export default InputSide;
In FormPage.js import the InputSide
component into it:
//FormPage.js
import React from 'react';
import styled from 'styled-components';
import DetailsBar from '../components/DetailsBar';
import InputSide from '../components/InputSide';
const FormPage = () => {
return (
<PageWrapper>
<PageHeadingWrapper>
<TextOne>Contact US</TextOne>
<TextTwo>
Any Question or remarks?
Just write us a message
</TextTwo>
</PageHeadingWrapper>
<FormContainer>
<DetailsBar />
<InputSide />
</FormContainer>
</PageWrapper>
);
};
export default FormPage;
Declaring The State Variables and
Input Handler Functions
Let us declare the state variables and add the functions for handling
the form inputs:
//InputSide.js
const InputSide = () => {
const [name, setName] = React.useState('');
const [email, setEmail] = React.useState('');
const [phone, setPhone] = React.useState('');
const [message, setMessage] =
React.useState('');
const [buttonLoading, setButtonLoading] =
React.useState(false);
const nameHandler = (e) => {
setName(e.target.value);
};
const emailHandler = (e) => {
setEmail(e.target.value);
};
const phoneHandler = (e) => {
setPhone(e.target.value);
};
const messageHandler = (e) => {
setMessage(e.target.value);
};
};
export default InputSide;
In this code, we have used the useState hook to create state variables for the form inputs and the loading state of the submit button.
We have also added the input handler functions.
Creating a Formgrid Account
Go to formgrid.dev
and sign up using Google or Email.
No credit card required.
Once you are logged in click New Form from your dashboard and give your form a name. Click Create Form.
You will be taken to your form's
Overview tab. Here you will find your
unique Endpoint URL. Copy it.
It looks like this:
https://formgrid.dev/api/f/your-form-id
This is the URL you will use to send your form submissions.
Every time someone submits your form, the data is sent to this endpoint, and Formgrid emails you the submission details instantly.
Unlike Formspree, you do not need to
configure anything else. Email notifications are enabled by default
using the email address you signed up with.
Handling Form Submission
Now let us add the function for handling form submission.
Replace the contents
of InputSide.js with the following:
//InputSide.js
import React from 'react';
import styled from 'styled-components';
const InputSide = () => {
const [name, setName] = React.useState('');
const [email, setEmail] = React.useState('');
const [phone, setPhone] = React.useState('');
const [message, setMessage] =
React.useState('');
const [buttonLoading, setButtonLoading] =
React.useState(false);
const nameHandler = (e) => {
setName(e.target.value);
};
const emailHandler = (e) => {
setEmail(e.target.value);
};
const phoneHandler = (e) => {
setPhone(e.target.value);
};
const messageHandler = (e) => {
setMessage(e.target.value);
};
const handleSubmit = async (e) => {
e.preventDefault();
setButtonLoading(true);
const response = await fetch(
'https://formgrid.dev/api/f/your-form-id',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name,
email,
phone,
message
}),
}
);
if (response.ok) {
setButtonLoading(false);
alert('Message sent successfully!');
} else {
setButtonLoading(false);
alert('Failed to send message');
}
};
return (
<InputSideWrapper onSubmit={handleSubmit}>
<InputWrapper>
<p>Name</p>
<Input
type="text"
placeholder="Allen Jones"
value={name}
onChange={nameHandler}
required
/>
</InputWrapper>
<InputWrapper>
<p>Email</p>
<Input
type="email"
placeholder="aljay126@gmail.com"
value={email}
onChange={emailHandler}
required
/>
</InputWrapper>
<InputWrapper>
<p>Phone</p>
<Input
type="number"
placeholder="+233546227893"
value={phone}
onChange={phoneHandler}
/>
</InputWrapper>
<InputWrapper>
<p>Message</p>
<MessageInput
placeholder="Write your message"
value={message}
onChange={messageHandler}
required
/>
</InputWrapper>
<SubMitButton
type="submit"
value={buttonLoading
? 'Sending...'
: 'Send Message'}
disabled={buttonLoading}
/>
</InputSideWrapper>
);
};
export default InputSide;
Replace your-form-id with the actual endpoint slug from your Formgrid form overview tab.
In this code, we use the fetch API to make a POST request to your Formgrid endpoint with the form data as JSON.
Formgrid receives the submission, saves it to your dashboard, and sends you an email notification instantly.
If the response is successful, the
if block handles it. Otherwise
the else block handles errors.
What happens after submission
When someone submits your form you will receive an email notification that looks like this:
New Submission: Contact Form
Name: Allen Jones
Email: aljay126@gmail.com
Phone: +233546227893
Message: Hello, I would like to
get in touch.
Submitted: 10:30 AM, March 22, 2026
Clean. Organized. Ready to act on.
You can also view all submissions in your Formgrid dashboard at any time.
No submission is ever lost, even if the email notification fails to arrive.
Adding spam protection
Formgrid includes built-in spam protection controls in the Security Settings section for each form (available on all plans).
You can protect your form in two ways:
- Add a honeypot field to your frontend form, and
- Enable protections in Form Details → Settings → Security Settings.
If you are adding a honeypot field manually, use this field name:
<input
type="text"
name="honeypot"
style={{ display: 'none' }}
tabIndex={-1}
autoComplete="off"
aria-hidden="true"
/>
OR
You can click on the form to visit the:
Form Details page. This is where you can manage everything related to that specific form.
Step 3: Navigate to the form Settings Tab
At the top of the Form Details page, you will see a row of tabs. Click on the Settings tab to open the form's configuration options.
Step 4: Scroll Down to the Security Settings Section
Within the Settings tab, scroll down until you reach the Security Settings section. This is where all of Formgrid's spam and abuse protection tools are located.
You will see the following options:
Enable CAPTCHA:
Adds a Google reCAPTCHA "I'm not a robot" verification step to your form. When enabled, anyone submitting your form will need to complete the CAPTCHA challenge before their submission goes through. Real human users can do this easily. Automated bots typically cannot, which stops the majority of spam submissions before they ever reach your inbox.
Enable Honeypot:
Adds a hidden field to your form that is completely invisible to real users but visible to bots. When a bot fills in the form, it fills in every field it can detect, including the hidden honeypot field. Formgrid detects this and automatically discards the submission as spam. Legitimate users never see or interact with this field, so it has no impact on the experience of real visitors filling in your form.
Allowed Domains (CORS):
Allows you to specify which domains are permitted to submit data to your form. If your form is embedded on a specific website, you can enter that domain here. Any submission coming from a different domain will be rejected. This is particularly effective against bots that submit directly to your form endpoint without visiting your website at all.
Rate Limiting:
Sets a maximum number of submissions allowed per IP address per minute. This prevents any single source from flooding your form with repeated submissions in a short period. If you expect normal users to submit your form occasionally, a limit of 3 to 5 submissions per minute per IP is a reasonable starting point.
Bots that automatically fill every field will fill in this hidden field. Formgrid detects this and automatically rejects the submission.
Creating The Success Page
The success page is where users will
be directed after form submission.
In src/pages/ create a new file
called SuccessPage.js and add
the following code:
//SuccessPage.js
import React from 'react';
import * as Icon from 'react-feather';
import { Fade } from 'react-reveal';
import styled from 'styled-components';
const MessageWrapper = styled.div`
margin-top: 150px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
`;
const SuccessMessage = styled.h2`
font-size: 25px;
color: rgb(8, 8, 63);
@media (max-width: 768px) {
font-size: 18px;
}
`;
const SuccessPage = () => {
return (
<React.Fragment>
<Fade bottom duration={700} distance="60px">
<MessageWrapper>
<Icon.CheckCircle
color="rgb(8, 8, 63)"
style={{ width: 50, height: 50 }}
/>
<SuccessMessage>
MESSAGE SENT SUCCESSFULLY
</SuccessMessage>
</MessageWrapper>
</Fade>
</React.Fragment>
);
};
export default SuccessPage;
Redirecting Users to The Success Page
We will use react-router-dom to handle
navigation between the form page and
the success page.
Run the following command to install it:
npm install react-router-dom
Open App.js and replace its contents
with the following:
//App.js
import React from 'react';
import {
BrowserRouter as Router,
Routes,
Route
} from 'react-router-dom';
import FormPage from './pages/FormPage';
import SuccessPage from './pages/SuccessPage';
const App = () => {
return (
<Router>
<div>
<Routes>
<Route path="/" element={<FormPage />} />
<Route
path="/success"
element={<SuccessPage />}
/>
</Routes>
</div>
</Router>
);
};
export default App;
Now update InputSide.js to redirect
the user to the success page after
a successful submission:
//InputSide.js
import React from 'react';
import { useNavigate } from 'react-router-dom';
const InputSide = () => {
const navigate = useNavigate();
const handleSubmit = async (e) => {
e.preventDefault();
setButtonLoading(true);
const response = await fetch(
'https://formgrid.dev/api/f/your-form-id',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name,
email,
phone,
message
}),
}
);
if (response.ok) {
setButtonLoading(false);
navigate('/success');
} else {
setButtonLoading(false);
alert('Failed to send message');
}
};
};
export default InputSide;
Why Use Formgrid Instead of Formspree
Both Formgrid and Formspree handle form
submissions reliably. Here is how
they compare:
| Feature | Formspree | Formgrid |
|---|---|---|
| Free submissions | 50/month | 50/month |
| Form builder | ❌ | ✅ |
| Self-hostable | ❌ | ✅ |
| Open source | ❌ | ✅ |
| Google Sheets native | $90/month | $16/month |
| Custom email templates | $90/month | $29/month |
| Submission dashboard | ✅ | ✅ |
| Spam protection | ✅ | ✅ |
Formgrid gives you everything Formspree
offers on the free plan, plus a no-code
form builder, self-hosting via Docker,
and significantly cheaper paid plans.
Conclusion
In this tutorial, we learnt how to create
a contact form in React using the Formgrid
API and Styled Components. We also added
react-router-dom for navigation between
the form page and the success page.
With Formgrid, your form submissions are
handled reliably, saved to a dashboard,
and emailed to you instantly. No
server-side code. No backend setup.
Just a simple endpoint URL.
👉 Try Formgrid free at formgrid.dev
Tags: #react #webdev #javascript
tutorial #formgrid
markdown
---









Top comments (0)