Next.js Conf 2024: Your app should be Live by Default – Watch Keynote

Sanity & Formspark Form Generator

By Roboto Studio & Jono Alford

On the fly form generation with Sanity & Formspark

Form Field Builder

import {FaAlignLeft} from 'react-icons/fa'

export default {
  name: 'formBuilder',
  title: 'Form Builder',
  icon: FaAlignLeft,
  type: 'object',
  fields: [
    {
      name: 'formFields',
      title: 'Form Fields',
      type: 'array',
      of: [{type: 'formFields'}],
    },
  ],
  preview: {
    prepare() {
      return {
        title: `Custom form setup`,
        subtitle: `Form Builder`,
        media: FaAlignLeft,
      }
    },
  },
}

Form Fields

export default {
  name: 'formFields',
  title: 'Form Fields',
  type: 'object',
  fields: [
    {
      name: 'required',
      title: 'Required',
      type: 'boolean',
      initialValue: false,
    },
    {
      name: 'fieldName',
      title: 'Field Name',
      type: 'string',
      validation: Rule => Rule.required(),
    },
    {
      name: 'placeholder',
      title: 'Placeholder',
      type: 'string',
      validation: Rule => Rule.required(),
    },
    {
      name: 'fieldId',
      title: 'Field ID',
      type: 'slug',
      options: {
        source: (doc, options) => options.parent.fieldName,
        maxLength: 200,
      },
      validation: Rule => Rule.required(),
    },
    {
      name: 'inputType',
      title: 'Input Type',
      type: 'string',
      initialValue: 'text',
      options: {
        layout: 'dropdown',
        list: [
          {value: 'text', title: 'Text input'},
          {value: 'email', title: 'Email'},
          {value: 'phone', title: 'Phone number'},
          {value: 'textArea', title: 'Text area'},
          {value: 'file', title: 'File upload'},
        ],
      },
      validation: Rule => Rule.required(),
    },
  ],
};

React Code

'use client';
import { useFormspark } from '@formspark/use-formspark';
import React, { FC, useState } from 'react';
import { useForm } from 'react-hook-form';

const InputField = ({ type, id, fieldName, required, placeholder, register }) => (
  <div>
    <label htmlFor={id}>{fieldName}</label>
    <div>
      <input
        type={type}
        id={id}
        required={!!required}
        placeholder={placeholder}
        aria-describedby={type}
        {...register(id)}
      />
    </div>
  </div>
);

export const FormBuilderBlock = ({ formFields, uid }) => {
  const [submitted, setSubmitted] = useState(false);
  const [submit, submitting] = useFormspark({ formId: 'YOUR_FORMSPARK_ID' });
  const { register, handleSubmit } = useForm();

  const onSubmit = (data) => {
    submit(data);
    setSubmitted(true);
  };

  if (submitted) {
    return (
      <div>
        <h1>Success box</h1>
      </div>
    );
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {Array.isArray(formFields) &&
        formFields.map((field) => {
          const { inputType, _key, fieldId, placeholder, _type, fieldName, required } = field ?? {};
          const { current } = fieldId ?? {};
          if (!inputType || !current) return null;

          if (inputType === 'textArea') {
            return (
              <div key={_key}>
                <label htmlFor={current}>{fieldName}</label>
                <div>
                  <textarea
                    id={current}
                    required={!!required}
                    placeholder={placeholder}
                    aria-describedby="textarea"
                    {...register(current)}
                  />
                </div>
              </div>
            );
          }

          return (
            <InputField
              key={_key}
              type={inputType}
              id={current}
              fieldName={fieldName}
              required={required}
              placeholder={placeholder}
              register={register}
            />
          );
        })}
      <button type="submit" disabled={submitting}>
        Submit
      </button>
    </form>
  );
};

You're coming from WordPress, and you want to get similar functionality to WP Forms but without the monstrous negative performance cost of actually using WordPress?

Boy gee, do we have some schema to help you out! This piece of code helps you to:

  • Generate forms on the fly
  • Add fields to a form
  • Remove fields from a form
  • Create textareas, text inputs, email inputs, etc
  • Generate an email when somebody submits

It's also using React Server Components, so feel free to drop the "use client" if you're using an older version of React or Next.js.

Oh, and if you're wondering why we're using Formspark? It's because it's got probably the best pricing tier for any form-handling system we've seen. Seriously 50,000 submissions for $25. I honestly don't know how they stay afloat as a business.

Want more great tips on how to structure your Sanity schema, Are you looking to build your company website with Sanity? Want to see what you can achieve with our team of experts?

☎️ Get a meeting booked in

Contributors

Other schemas by authors

Cursor Prompt

Thinking about getting started with AI? Well we're just going to share our latest and greatest prompt so you don't have to do the hard work

Go to Cursor Prompt

Portable Text Mock Content

If you're looking to mockup portable text in Storybook without a Sanity backend, this is the schema you're looking for

Go to Portable Text Mock Content