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.