Joint session with Vercel: How to build intelligent storefronts (May 15th)

Sanity & Formspark Form Generator

On the fly form generation with Sanity & Formspark

By Roboto Studio, Jono Alford & Hrithik Prasad


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

Roboto Studio

The best editorial experiences on the web

Visit Roboto Studio's profile

Jono Alford

Founder @ Roboto Studio - The Sanity & Next.js experts

Visit Jono Alford's profile

Hrithik Prasad

Technical Lead at Roboto Studio

India

Visit Hrithik Prasad's profile