Live, interactive examples showcasing all features of the SonicJS Forms system.
- Basic Form Examples
- Multi-Page Wizards
- Advanced Components
- Validation & Conditional Logic
- Headless Integration
- Custom Styling
- File Uploads
- Embedding Examples
What it demonstrates: Basic text fields, email validation, text area, and submit button.
http://localhost:8787/forms/simple_contact
{
"display": "form",
"components": [
{
"type": "textfield",
"key": "name",
"label": "Full Name",
"placeholder": "Enter your name",
"validate": {
"required": true,
"minLength": 2,
"maxLength": 100
}
},
{
"type": "email",
"key": "email",
"label": "Email Address",
"placeholder": "you@example.com",
"validate": {
"required": true
}
},
{
"type": "textarea",
"key": "message",
"label": "Message",
"placeholder": "How can we help you?",
"rows": 5,
"validate": {
"required": true,
"minLength": 10
}
},
{
"type": "button",
"action": "submit",
"label": "Send Message",
"theme": "primary"
}
]
}// Via API
await fetch('/admin/forms', {
method: 'POST',
body: new URLSearchParams({
name: 'simple_contact',
displayName: 'Simple Contact Form',
category: 'contact'
})
});
// Then use builder to add componentsWhat it demonstrates: Minimal form with checkbox for consent.
http://localhost:8787/forms/newsletter_signup
{
"display": "form",
"components": [
{
"type": "email",
"key": "email",
"label": "Email Address",
"validate": { "required": true }
},
{
"type": "checkbox",
"key": "consent",
"label": "I agree to receive marketing emails",
"validate": { "required": true }
},
{
"type": "button",
"action": "submit",
"label": "Subscribe"
}
]
}What it demonstrates: Radio buttons, select dropdowns, ratings.
{
"display": "form",
"components": [
{
"type": "radio",
"key": "satisfaction",
"label": "How satisfied are you with our service?",
"values": [
{ "label": "Very Satisfied", "value": "very_satisfied" },
{ "label": "Satisfied", "value": "satisfied" },
{ "label": "Neutral", "value": "neutral" },
{ "label": "Dissatisfied", "value": "dissatisfied" },
{ "label": "Very Dissatisfied", "value": "very_dissatisfied" }
],
"validate": { "required": true }
},
{
"type": "select",
"key": "category",
"label": "What category best describes your feedback?",
"data": {
"values": [
{ "label": "Product Quality", "value": "quality" },
{ "label": "Customer Service", "value": "service" },
{ "label": "Pricing", "value": "pricing" },
{ "label": "Delivery", "value": "delivery" },
{ "label": "Other", "value": "other" }
]
}
},
{
"type": "textarea",
"key": "comments",
"label": "Additional Comments",
"rows": 4
}
]
}What it demonstrates: Multi-step form with personal info → experience → upload resume.
http://localhost:8787/forms/job_application
{
"display": "wizard",
"components": [
{
"type": "panel",
"key": "personalInfo",
"title": "Personal Information",
"components": [
{
"type": "textfield",
"key": "firstName",
"label": "First Name",
"validate": { "required": true }
},
{
"type": "textfield",
"key": "lastName",
"label": "Last Name",
"validate": { "required": true }
},
{
"type": "email",
"key": "email",
"label": "Email",
"validate": { "required": true }
},
{
"type": "phoneNumber",
"key": "phone",
"label": "Phone Number"
}
]
},
{
"type": "panel",
"key": "experience",
"title": "Work Experience",
"components": [
{
"type": "textfield",
"key": "currentPosition",
"label": "Current Position"
},
{
"type": "textfield",
"key": "currentCompany",
"label": "Current Company"
},
{
"type": "number",
"key": "yearsExperience",
"label": "Years of Experience",
"validate": { "min": 0, "max": 50 }
}
]
},
{
"type": "panel",
"key": "documents",
"title": "Documents",
"components": [
{
"type": "file",
"key": "resume",
"label": "Upload Resume",
"storage": "r2",
"fileTypes": [
{ "label": "PDF", "value": "application/pdf" },
{ "label": "Word", "value": "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }
],
"validate": { "required": true }
},
{
"type": "textarea",
"key": "coverLetter",
"label": "Cover Letter",
"rows": 8
}
]
}
]
}- ✅ Previous/Next buttons auto-generated
- ✅ Progress indicator shows current step
- ✅ Per-page validation (can't proceed with errors)
- ✅ Data persists when going back/forward
What it demonstrates: Conditional pages based on previous answers.
{
"display": "wizard",
"components": [
{
"type": "panel",
"key": "productType",
"title": "Product Type",
"components": [
{
"type": "select",
"key": "type",
"label": "What product did you purchase?",
"data": {
"values": [
{ "label": "Software", "value": "software" },
{ "label": "Hardware", "value": "hardware" }
]
}
}
]
},
{
"type": "panel",
"key": "softwareDetails",
"title": "Software Details",
"conditional": {
"show": true,
"when": "type",
"eq": "software"
},
"components": [
{
"type": "textfield",
"key": "licenseKey",
"label": "License Key"
}
]
},
{
"type": "panel",
"key": "hardwareDetails",
"title": "Hardware Details",
"conditional": {
"show": true,
"when": "type",
"eq": "hardware"
},
"components": [
{
"type": "textfield",
"key": "serialNumber",
"label": "Serial Number"
}
]
}
]
}What it demonstrates: Google Maps autocomplete for addresses.
{
"type": "address",
"key": "address",
"label": "Delivery Address",
"map": {
"key": "YOUR_GOOGLE_MAPS_API_KEY",
"region": "US"
},
"provider": "google",
"validate": { "required": true }
}- Get Google Maps API key
- Enable Places API and Maps JavaScript API
- Add key to component's
map.keyfield in builder
What it demonstrates: Digital signature capture.
{
"type": "signature",
"key": "signature",
"label": "Sign Here",
"width": "100%",
"height": "150px",
"penColor": "black",
"backgroundColor": "rgb(245,245,245)",
"validate": { "required": true }
}What it demonstrates: Date selection with validation.
{
"type": "datetime",
"key": "appointmentDate",
"label": "Preferred Appointment Date",
"format": "yyyy-MM-dd",
"enableDate": true,
"enableTime": false,
"validate": {
"required": true
},
"datePicker": {
"minDate": "2026-01-01",
"maxDate": "2026-12-31",
"disable": ["2026-12-25", "2026-01-01"]
}
}What it demonstrates: Show/hide fields based on user input.
{
"components": [
{
"type": "checkbox",
"key": "hasCompany",
"label": "I'm registering on behalf of a company"
},
{
"type": "textfield",
"key": "companyName",
"label": "Company Name",
"conditional": {
"show": true,
"when": "hasCompany",
"eq": true
},
"validate": { "required": true }
},
{
"type": "textfield",
"key": "taxId",
"label": "Tax ID",
"conditional": {
"show": true,
"when": "hasCompany",
"eq": true
}
}
]
}What it demonstrates: Custom validation patterns.
{
"components": [
{
"type": "textfield",
"key": "username",
"label": "Username",
"validate": {
"required": true,
"minLength": 3,
"maxLength": 20,
"pattern": "^[a-zA-Z0-9_]+$",
"customMessage": "Username must be 3-20 characters and contain only letters, numbers, and underscores"
}
},
{
"type": "password",
"key": "password",
"label": "Password",
"validate": {
"required": true,
"minLength": 8,
"pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]",
"customMessage": "Password must contain uppercase, lowercase, number, and special character"
}
},
{
"type": "password",
"key": "confirmPassword",
"label": "Confirm Password",
"validate": {
"required": true,
"custom": "valid = (input === data.password) ? true : 'Passwords must match';"
}
}
]
}Live Demo: See full code in FORMS_HEADLESS_FRONTEND.md
import { Form } from '@formio/react';
function ContactForm() {
const [schema, setSchema] = useState(null);
useEffect(() => {
fetch('https://your-api.com/forms/contact/schema')
.then(r => r.json())
.then(data => setSchema(data.schema));
}, []);
const handleSubmit = async (submission) => {
await fetch('https://your-api.com/api/forms/contact/submit', {
method: 'POST',
body: JSON.stringify({ data: submission.data })
});
};
return schema ? <Form form={schema} onSubmit={handleSubmit} /> : <div>Loading...</div>;
}---
// Fetch form at build time
const response = await fetch('https://your-api.com/forms/contact/schema');
const formData = await response.json();
---
<div id="contact-form"></div>
<script define:vars={{ formData }}>
import('https://cdn.form.io/formiojs/formio.full.min.js').then(({ default: Formio }) => {
Formio.createForm(document.getElementById('contact-form'), formData.schema);
});
</script>What it demonstrates: 100% custom styles, no Form.io defaults.
<!DOCTYPE html>
<html>
<head>
<style>
/* Custom styles only - no Form.io CSS */
.formio-component { margin-bottom: 1.5rem; }
.formio-component label {
display: block;
font-weight: 600;
margin-bottom: 0.5rem;
}
.formio-component input,
.formio-component textarea {
width: 100%;
padding: 0.75rem;
border: 2px solid #e0e0e0;
border-radius: 8px;
}
.formio-component button[type="submit"] {
background: #3b82f6;
color: white;
padding: 0.875rem 2rem;
border: none;
border-radius: 8px;
cursor: pointer;
}
</style>
</head>
<body>
<div id="form"></div>
<!-- Only include JS, not CSS -->
<script src="https://cdn.form.io/formiojs/formio.full.min.js"></script>
<script>
fetch('/forms/contact/schema')
.then(r => r.json())
.then(data => Formio.createForm(document.getElementById('form'), data.schema));
</script>
</body>
</html>See: Forms Embedding Guide for complete styling examples.
<style>
:root {
--form-primary: #8b5cf6;
--form-border: #d1d5db;
--form-radius: 12px;
}
.formio-component input,
.formio-component select {
border: 2px solid var(--form-border);
border-radius: var(--form-radius);
}
.formio-component button[type="submit"] {
background: var(--form-primary);
border-radius: var(--form-radius);
}
</style>What it demonstrates: File upload to Cloudflare R2.
{
"components": [
{
"type": "textfield",
"key": "fullName",
"label": "Full Name",
"validate": { "required": true }
},
{
"type": "email",
"key": "email",
"label": "Email",
"validate": { "required": true }
},
{
"type": "file",
"key": "resume",
"label": "Upload Resume",
"storage": "r2",
"url": "/api/forms/upload",
"fileTypes": [
{ "label": "PDF", "value": "application/pdf" },
{ "label": "Word", "value": "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }
],
"fileMaxSize": "5MB",
"validate": { "required": true }
}
]
}File uploads are automatically stored in Cloudflare R2 when storage: "r2" is set. Files are tracked in the form_files table.
{
"type": "file",
"key": "documents",
"label": "Upload Documents",
"multiple": true,
"storage": "r2",
"fileMaxSize": "10MB",
"fileMinSize": "1KB"
}Quickest way to embed a form:
<iframe
src="https://your-sonicjs-site.com/forms/contact_form"
width="100%"
height="600"
frameborder="0"
style="border: 1px solid #e0e0e0; border-radius: 8px;"
></iframe>Pros:
- ✅ Instant embed, no coding required
- ✅ Form updates automatically
Cons:
⚠️ Limited styling control⚠️ Can't match your site's exact design
Best for matching your site's design:
<div id="my-form"></div>
<script src="https://cdn.form.io/formiojs/formio.full.min.js"></script>
<script>
fetch('https://your-api.com/forms/contact/schema')
.then(r => r.json())
.then(data => {
Formio.createForm(document.getElementById('my-form'), data.schema);
});
</script>
<style>
/* Your site's styles */
#my-form .formio-component input {
font-family: inherit;
border: 2px solid var(--your-brand-color);
}
</style>-
Go to the builder:
http://localhost:8787/admin/forms/new -
Create a simple form:
- Name:
test_form - Display Name:
My Test Form
- Name:
-
Add components:
- Drag "Text Field" from sidebar
- Configure label and validation
- Drag "Email" field
- Drag "Text Area"
- Drag "Button" (Submit)
-
Save and test:
- Click "Save Form"
- Visit
/forms/test_form - Submit and check
/admin/forms/{id}/submissions
{
"display": "wizard",
"components": [
{
"type": "panel",
"key": "attendeeInfo",
"title": "Attendee Information",
"components": [
{ "type": "textfield", "key": "name", "label": "Full Name" },
{ "type": "email", "key": "email", "label": "Email" },
{ "type": "phoneNumber", "key": "phone", "label": "Phone" }
]
},
{
"type": "panel",
"key": "ticketSelection",
"title": "Ticket Selection",
"components": [
{
"type": "select",
"key": "ticketType",
"label": "Ticket Type",
"data": {
"values": [
{ "label": "Early Bird - $99", "value": "early_bird" },
{ "label": "Regular - $149", "value": "regular" },
{ "label": "VIP - $299", "value": "vip" }
]
}
},
{
"type": "number",
"key": "quantity",
"label": "Number of Tickets",
"defaultValue": 1,
"validate": { "min": 1, "max": 10 }
}
]
},
{
"type": "panel",
"key": "payment",
"title": "Payment",
"components": [
{
"type": "htmlelement",
"key": "totalPrice",
"tag": "div",
"content": "<p><strong>Total:</strong> <span id='total'>$0</span></p>"
}
]
}
]
}- Keep forms under 10 fields when possible
- Use wizards for forms >6 fields
- Group related fields in panels
- Use clear, actionable button text
- Provide helpful placeholder text
- Mark required fields clearly
- Provide instant feedback on errors
- Use appropriate input types (email, tel, number)
- Add custom validation messages
- Validate before allowing progression
- Show progress in wizards
- Allow users to go back and edit
- Persist data between pages
- Show clear success messages
- Handle errors gracefully
- Lazy load forms with scripts
- Use CDN for Form.io library
- Minimize custom CSS
- Cache form schemas
- Optimize file upload sizes
- Quick Reference - One-page cheat sheet
- Embedding Guide - How to embed forms with custom styling
- Headless Frontend - React, Astro, Angular, Vue examples
- API Reference - Programmatic form creation
- Wizard Forms - Multi-page forms guide
- Testing Guide - E2E, unit, and manual tests
Can't find what you're looking for?
- 📖 Check Form.io Examples for more Form.io features
- 💬 Join our Discord for help
- 🐛 Open a GitHub issue for documentation requests
- 📧 Contact support@sonicjs.com
Last Updated: January 2026
Version: 1.0.0