Skip to main content
Create the Form and Form Field collections first. See Custom form modeling in the dashboard.

React implementation

import { useState } from 'react';

const CustomForm = ({ form }) => {
  const [formData, setFormData] = useState({});
  const [errors, setErrors] = useState({});
  const [submitted, setSubmitted] = useState(false);

  const validateField = (field, value) => {
    if (field.required && !value) {
      return field.validation_error_message || 'This field is required';
    }
    if (field.input_type === 'email' && value) {
      const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      if (!emailRegex.test(value)) {
        return 'Please enter a valid email address';
      }
    }
    if (!field.allow_numbers && /\d/.test(value)) {
      return 'Numbers are not allowed';
    }
    if (!field.allow_special_symbols && /[!@#$%^&*(),.?":{}|<>]/.test(value)) {
      return 'Special characters are not allowed';
    }
    return null;
  };

  const handleChange = (field, value) => {
    setFormData(prev => ({ ...prev, [field.field_name]: value }));
    const error = validateField(field, value);
    setErrors(prev => ({ ...prev, [field.field_name]: error }));
  };

  const handleSubmit = async (e) => {
    e.preventDefault();

    const newErrors = {};
    form.fields.forEach(field => {
      const error = validateField(field, formData[field.field_name]);
      if (error) newErrors[field.field_name] = error;
    });

    if (Object.keys(newErrors).length > 0) {
      setErrors(newErrors);
      return;
    }

    try {
      await fetch(form.submission_endpoint, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(formData),
      });
      setSubmitted(true);
    } catch (error) {
      console.error('Form submission failed:', error);
    }
  };

  if (submitted) {
    return <div className="success-message">{form.success_message}</div>;
  }

  return (
    <form onSubmit={handleSubmit}>
      <h2>{form.form_title}</h2>
      {form.fields.map(field => (
        <div key={field.field_name} className="form-field">
          <label htmlFor={field.field_name}>
            {field.field_label}
            {field.required && <span className="required">*</span>}
          </label>
          <input
            type={field.input_type || 'text'}
            id={field.field_name}
            name={field.field_name}
            placeholder={field.placeholder}
            value={formData[field.field_name] || ''}
            onChange={(e) => handleChange(field, e.target.value)}
            className={errors[field.field_name] ? 'error' : ''}
          />
          {errors[field.field_name] && (
            <span className="error-message">{errors[field.field_name]}</span>
          )}
        </div>
      ))}
      <button type="submit">{form.submit_button_text}</button>
    </form>
  );
};

Notes

  • Submit to a backend endpoint you control.
  • Add spam protection (CAPTCHA), rate limits, and server-side validation.
  • Store submissions securely and consider audit logging.