React Contact Form (2023): A Low-Code Full-Stack Tutorial

Whether you're a freelancer or an agency, creating a contact form for your website is important to building a successful online presence. It allows your users to reach out to you with their queries, feedback, and suggestions: it's a great way to gather valuable insights about your audience. This article will discuss how to create a contact form using React and how to persist the collected data with Rowy.

React Contact Form

React Components

The first step in creating a contact form using React is to create a basic React component. This component should include all the necessary fields, such as name, email, and message, along with a submit button that will trigger the form submission:

import React, { useState } from 'react';

const App = () => {

    const [ name, setName ] = useState('')
    const [ email, setEmail ] = useState('')
    const [ message, setMessage ] = useState('')

    function handleSubmit(e) => {
        console.log(name, email, message)

  return (
        <label htmlFor="name">Name</label>
        <input type="text" id="name" name="name" value={name} onChange={e => setName(}/>

        <label htmlFor="email">Email</label>
        <input type="email" id="email" name="email" value={email} onChange={e => setEmail(}/>

        <label htmlFor="message">Message</label>
        <textarea id="message" name="message" value={message} onChange={e => setMessage(}/>

      <button type="submit" onClick={handleSubmit}>Submit</button>

export default App;

Which will give us the following HTML form:

React Contact Form 0.jpg

Security Concerns

When creating a contact form, it's essential to consider security. One of the most common security concerns is preventing bots from submitting the form and spamming your inbox. To accomplish this, you can use a Captcha and a honeypot.

A Captcha is a visual test that is easy for humans to pass but difficult for bots. Google's reCAPTCHA is a popular Captcha library you can easily incorporate in a React app with the react-google-recaptcha npm package:


const [ recaptcha, setRecaptcha ] = useState('')
const [ isVerified, setIsVerified ] = useState('')

function onChange(value) {
    if (value) {
    } else {

function handleSubmit(e) => {

        console.log(name, email, message)


All you'll need is a Recaptcha API key you can obtain by creating an account on Google's reCAPTCHA website.

Additionally, the honeypot technique relies on a hidden form field that bots will fill in, but humans won't see:

const [fake_field, setFakeField] = useState('')

function handleSubmit(){
    if(fake_field !== ''){
        console.error("it's a bot!")

<input type="hidden" name="fake_field" value={fake_field} onChange={e => setFakeField(}/>

Storing & Processing Contact Data

Table schema

Once the form is submitted, the data needs to be stored somewhere. Preferably, in a secure database. That's where Rowy comes into play.

Rowy is a low-code backend platform built on top of Firebase that makes it easy to create databases in a visual way and run custom code as needed. To get started, create an account on Rowy and create a new table.

To create a basic table schema for a contact form, you will need columns for the name, email, message, as well as a sentAt to know when the form was submitted:

Rowy contact form table 1.jpg

Sending a confirmation email

Rowy is more than a database―it also allows you to process the data in convenient cloud functions: once the data is stored in the database, you can for example create a derivative column to send a confirmation email to the user. This email can include a thank-you message and any other relevant information.

Rowy derivative column 2.jpg

A derivative column updates when its source columns are updated. In this case, whenever a new row is added to the table, the sendEmail column will be updated and send a confirmation email to the user using Sengrid's API:

const derivative:Derivative = async ({row,ref,db,storage,auth,logging})=>{
    if(!row.confirmationSent && && row.message){
        const API_KEY = rowy.secrets.SENDGRID_API_KEY

        const name = ? : "kind stranger" 

        await fetch("", {
            method: "POST",
            headers: {
                "Authorization": `Bearer ${API_KEY}`
            body: JSON.stringify({
                to: [
                from: {
                    email: your_registered_email_address,
                    name: your_company_name
                subject: 'Contact Form Confirmation',
                content: [
                        type: 'text/html',
                        value: `<p>Thank you for registering ${name}. We'll get back to you soon!</p><p>%open-track%</p>`

        row.confirmationSent = true

Black-listing contact data

To ensure that your users' data is protected, it's important to include a way to blacklist contacts manually server-side. For this, You can create a toggle column to flag contact data as needed. It's required by law you don't hold on to user data for longer than necessary, so you can use this column to delete the data after a certain period of time.

A Rowy toggle column presents itself as a toggle button. In this case, the action column will flag the row as blacklisted when clicked:

a rowy action column that switches the blacklisting flag on and off 3.jpg

Saving Contact Data

Now our backend is ready, we need to save the contact data when the contact form is submitted client-side. Fortunately, Rowy seamlessly integrates with the Firebase API.

Using the Firebase API

Saving contact data is as simple as sending a POST request to the Firebase API. You can use the fetch API to do this from any React component:

import { initializeApp } from "firebase/app";
import { getFirestore, addDoc, colelction } from "firebase/firestore"; 

const firebaseConfig = YOUR_FIREBASE_CONFIG
const app = initializeApp(firebaseConfig)
const db = getFirestore(app)
const collection_name = "contacts"

function save() => addDoc(collection(db, collection_name), {
    name: name,
    email: email,
    message: message,
    timestamp: new Date()

function handleSubmit(e) => {

    if(isVerified && fake_field !== ''){
        await save()

Every time you press the submit button, a new row will be added to the contacts collection in your Firebase database:

a rowy table with contact data 4.jpg

Using webhooks to unsubscribe

Rowy also provides custom webhooks to trigger a script when an event occurs. In this case, we can use a webhook URL to unsubscribe users from our contact list when they request to be removed from the database.

First we create a webhook URL by clicking on the Webhooks icon and adding a basic webhook:

a rowy webhook 5.jpg

We add the following code in the webhook settings that will blacklist the current row from the database when the webhook is triggered:

const basicParser: Parser = async({req, db, ref, logging}) => {
  const { body } = req;

  const contacts = await db
    .where("email", "==",

  const contact =[0]

  contact.ref.update({isBlacklisted: true})

  return true;

Finally, we can add the link to the unsubscribe webhook in all the emails we send.

Join The Rowy Community

Simple, right? Creating a simple contact form using React and Rowy is a piece of cake and you can create complex data processing pipelines with little to no code. Join the Rowy community on Discord to build more useful software tools with minimal code.

Get started with Rowy in minutes

Continue reading

Browse all