top of page

Form Builder with React, React-hook-form, and Contentful Headless CMS

Title: Build a Form Builder with React, React-hook-form, and Contentful Headless CMS


Introduction


In this guide, we'll be integrating React-hook-form with Contentful Headless CMS to create a powerful form builder that can store and manage form schema, submissions, and validations.


Here's a step-by-step guide to setting up a form builder with React, React-hook-form, and Contentful.


Prerequisites


1. A Contentful account and a content model for form schema.

2. React components implemented as needed.


Form Schema

This is the recommended high-level schema to implement in Contentful collections


interface FormConfig {
  formId: string;
  fields: FieldConfig[];
  layout?: FormLayout;
  logic?: FormLogic;
}

interface FieldConfig {
  key: string;
  type: InputType;
  label?: string;
  isDisabled?: boolean;
  isRequired?: boolean;
  options?: Option[];
  defaultValue?: any;
  children?: { fields: FieldConfig[] };
  Field?: Field;
}



## Step 1: Install dependencies


First, let's install the necessary dependencies for our project:

npm install react-hook-form contentful

## Step 2: Set up Contentful client


Now, let's set up the Contentful client to fetch form schema data:

// contentfulClient.js
import { createClient } from 'contentful';

const client = createClient({
  space: process.env.CONTENTFUL_SPACE_ID,
  accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
});

export default client;

Don't forget to store your Contentful Space ID and Access Token in your project's environment variables.


## Step 3: Fetch form schema from Contentful


Next, we'll fetch the form schema from Contentful and pass it as a prop to our form component:

// FormContainer.js
import React, { useState, useEffect } from 'react';
import contentfulClient from './contentfulClient';
import FormBuilder from './FormBuilder';

const FormContainer = () => {
  const [formSchema, setFormSchema] = useState(null);

  useEffect(() => {
    contentfulClient.getEntries({ content_type: 'formSchema' })
      .then(response => {
        if (response.items.length > 0) {
          setFormSchema(response.items[0].fields);
        }
      })
      .catch(error => console.error(error));
  }, []);

  return (
    <div>
      {formSchema ? <FormBuilder schema={formSchema} /> : <p>Loading...</p>}
    </div>
  );
};

export default FormContainer;

## Step 4: Create FormBuilder component


Now let's create the `FormBuilder` component that renders the form fields based on the fetched schema:

// FormBuilder.js
import React from 'react';
import { useForm } from 'react-hook-form';

const FormBuilder = ({ schema }) => {
  const { register, handleSubmit, formState: { errors } } = useForm();

  const onSubmit = (data) => {
    // Process form data here
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {schema.fields.map((field, index) => (
        <div key={index}>
          <label htmlFor={field.name}>{field.label}</label>
          <input
            id={field.name}
            {...register(field.name, {
              required: field.required,
              pattern: field.validationPattern,
            })}
          />
          {errors[field.name] && <p>{field.errorMessage}</p>}
        </div>
      ))}
      <button type="submit">Submit</button>
    </form>
  );
};

export default FormBuilder;

## Step 5: Save form submissions to Contentful


Finally, let's save the form submissions back to Contentful:


// FormBuilder.js
import React from 'react';
import { useForm } from 'react-hook-form';
import contentfulClient from './contentfulClient';

const FormBuilder = ({ schema }) => {
  const { register, handleSubmit, formState: { errors } } } = useForm();

  const onSubmit = (data) => {
    // Create a new form submission entry in Contentful
    contentfulClient.getSpace(process.env.CONTENTFUL_SPACE_ID)
      .then(space => space.createEntry('formSubmission', {
        fields: {
          formData: {
            'en-US': JSON.stringify(data)
          }
        }
      }))
      .then(entry => {
        console.log('Form submission saved:', entry.sys.id);
        // Perform any other actions you want after form submission (e.g., show a success message)
      })
      .catch(error => console.error('Error saving form submission:', error));
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {schema.fields.map((field, index) => (
        <div key={index}>
          <label htmlFor={field.name}>{field.label}</label>
          <input
            id={field.name}
            {...register(field.name, {
              required: field.required,
              pattern: field.validationPattern,
            })}
          />
          {errors[field.name] && <p>{field.errorMessage}</p>}
        </div>
      ))}
      <button type="submit">Submit</button>
    </form>
  );
};

export default FormBuilder;




With these steps, you have successfully set up a form builder using React, React-hook-form, and Contentful Headless CMS. This form builder will render form fields based on the fetched schema from Contentful and save form submissions back to Contentful as well.


Tutim - The open-source headless wizard form infrastructure


You can also use Tutim as your fully-featured form builder with Contentful.


An open-source, headless, API-first platform that empowers developers and product teams to create and optimize in-app user flows, such as new user onboarding wizards.


Tutim supports multi-step forms, conditional branching, custom validation, localization, and more – all through a JSON schema.


bottom of page