Dynamic form renderer component based on JSON Schema with drag-and-drop form builder
npm install fendererbash
npm install fenderer
`
Peer Dependencies
Make sure you have these installed:
`bash
npm install react react-dom @mui/material @mui/icons-material @emotion/react @emotion/styled
`
Configuration (Required)
Before using any form components, you must configure the library with your backend URL:
`tsx
import { configureOrchaForm } from "fenderer";
// Configure once in your app's entry point (e.g., index.tsx or App.tsx)
configureOrchaForm({
backendUrl: "https://your-backend-api.com/api",
defaultHeaders: {
Authorization: "Bearer your-api-token",
},
timeout: 30000, // Optional: request timeout in ms
});
`
How Webhook Proxy Works
When a form has a webhookUrl configured:
1. With webhookUrl: Form data is sent to:
- ${backendUrl}/webhook/proxy/{formName} (if formName is provided)
- ${backendUrl}/webhook/proxy (if no formName)
With payload:
`json
{
"webhookUrl": "https://webhook.site/your-url",
"method": "POST",
"data": { "form": "data" },
"headers": {},
"originalEndpoint": "https://api.example.com/fallback",
"formName": "contact-form" // included if formName is set
}
`
2. Without webhookUrl: Form data is sent directly to the endpoint URL.
Your backend should handle the /webhook/proxy and /webhook/proxy/:formName endpoints to forward requests to the actual webhook.
Quick Start
``tsx
import { FormRenderer, configureOrchaForm } from "fenderer";
// Configure first
configureOrchaForm({
backendUrl: "https://your-backend.com/api",
});
Components
$3
Create forms visually with drag-and-drop interface:
`tsx
import { FormBuilder, configureOrchaForm } from 'fenderer';
// Configure first
configureOrchaForm({
backendUrl: 'https://your-backend.com/api'
});
function App() {
return (
initialFormTitle="Contact Form"
initialDescription="Please fill out this form"
initialTargetUrl="https://api.example.com/contact"
initialWebhookUrl="https://webhook.site/your-unique-url"
initialFormName="contact-form" // For webhook routing: /webhook/proxy/contact-form
initialRedirectOnSuccess="https://example.com/thank-you"
initialRedirectOnFailed="https://example.com/error"
onSchemaChange={(schema) => {
console.log('Generated schema:', schema);
}}
onFormSubmit={(data, schema) => {
console.log('Form submitted:', data);
}}
/>
);
}
`
#### Edit Existing Forms with initialSchema
The initialSchema prop allows you to pre-populate the FormBuilder with an existing form schema for editing. This is perfect for loading saved forms, templates, or when you want to modify an existing form.
`tsx
const existingFormSchema = {
schema: {
title: "Customer Feedback Form",
description: "Help us improve our service",
type: "object",
properties: {
customerName: { type: "string", title: "Customer Name" },
email: { type: "string", title: "Email Address", format: "email" },
serviceRating: {
type: "integer",
title: "Service Rating",
minimum: 1,
maximum: 5,
},
feedbackType: {
type: "string",
title: "Feedback Type",
enum: ["Compliment", "Complaint", "Suggestion", "Question"],
},
message: { type: "string", title: "Your Message" },
newsletter: { type: "boolean", title: "Subscribe to Newsletter" },
},
required: ["customerName", "email", "serviceRating", "message"],
},
uiSchema: {
serviceRating: { "ui:widget": "range" },
feedbackType: { "ui:widget": "radio" },
message: { "ui:widget": "textarea", "ui:options": { rows: 4 } },
newsletter: { "ui:widget": "checkbox" },
},
submission: {
endpoint: "https://api.company.com/feedback",
method: "POST",
webhookUrl: "https://webhook.site/feedback-processor",
formName: "customer-feedback",
},
};
initialSchema={existingFormSchema}
onSchemaChange={(updatedSchema) => {
console.log('Form updated:', updatedSchema);
// Save the updated schema to your backend/database
}}
onFormSubmit={(data, schema) => {
console.log('Test submission:', data);
}}
/>
`
What happens when using initialSchema:
1. Form Configuration Pre-filled: All form settings (title, description, target URL, webhook URL, form name, HTTP method) are automatically loaded from the schema
2. Fields Converted: Schema properties are converted back to editable field objects in the builder
3. Live Preview Ready: The form is immediately editable in the live preview with appropriate default values
4. Full Editability: You can modify any aspect:
- ✅ Form title, description, and configuration
- ✅ Target URL, webhook URL, form name, HTTP method
- ✅ Add, remove, or edit existing fields
- ✅ Change field types, labels, validation, and UI widgets
- ✅ Test the form in live preview
Field Type Detection: The FormBuilder automatically detects field types from the schema:
- string with format: "email" → Email field
- string with enum → Selection dropdown/radio
- array with items.enum → Multi-select checkboxes
- boolean → Checkbox
- integer/number → Number input
- string with ui:widget: "textarea" → Textarea
- string (default) → Text input
#### FormBuilder Props
- initialFormTitle?: Initial form title
- initialDescription?: Initial form description
- initialTargetUrl?: Initial target URL for submissions
- initialWebhookUrl?: Initial webhook URL (optional)
- initialHttpMethod?: Initial HTTP method (default: "POST")
- initialFormName?: Initial form name for webhook routing
- initialRedirectOnSuccess?: Initial redirect URL for successful submissions
- initialRedirectOnFailed?: Initial redirect URL for failed submissions
- initialSchema?: Pre-populate form builder with existing FormSchema
- onSchemaChange?: Callback when schema changes
- onFormSubmit?: Callback when form is submitted from live preview
- showLivePreview?: Show live form preview (default: true)
- showExportButton?: Show export schema button (default: true)
- containerStyle?: Custom styles for main container
- paletteStyle?: Custom styles for field palette
- dropAreaStyle?: Custom styles for drop area
- previewStyle?: Custom styles for live preview
tsx
import { FormRenderer, configureOrchaForm } from "fenderer";
// Configure first
configureOrchaForm({
backendUrl: "https://your-backend.com/api",
});
const formSchema = {
schema: {
title: "Contact Form",
type: "object",
properties: {
name: { type: "string", title: "Full Name" },
email: { type: "string", title: "Email", format: "email" },
},
required: ["name", "email"],
},
uiSchema: {
email: { "ui:placeholder": "Enter your email" },
},
submission: {
endpoint: "https://api.example.com/contact",
method: "POST",
webhookUrl: "https://webhook.site/your-unique-url", // Optional: will be proxied
redirectOnSuccess: "https://example.com/thank-you", // Optional: redirect after success
redirectOnFailed: "https://example.com/error", // Optional: redirect after error
},
};
function App() {
return (
formSchema={formSchema}
onSubmitSuccess={(data) => console.log("Success:", data)}
onSubmitError={(error) => console.log("Error:", error)}
/>
);
}
`
API Reference
$3
- formSchema: FormSchema - The complete form configuration
- onSubmitSuccess?: Function called on successful submission
- onSubmitError?: Function called on submission error
- onDataChange?: Function called when form data changes
- initialData?: Initial form data
- showSuccessAlert?: Show success SweetAlert (default: true)
- showErrorAlert?: Show error SweetAlert (default: true)
- customSuccessMessage?: Custom success message
- customErrorMessage?: Custom error message
- disabled?: Disable the entire form
$3
`tsx
interface FormSchema {
schema: JSONSchema7; // JSON Schema for form validation
uiSchema: Record; // UI customization
submission: {
endpoint: string; // Target URL for form submission
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
webhookUrl?: string; // Optional webhook URL (takes priority)
headers?: Record;
transformData?: (data: any) => any;
formName?: string; // Optional form name for webhook routing (/webhook/proxy/{formName})
redirectOnSuccess?: string; // Optional URL to redirect to after successful submission
redirectOnFailed?: string; // Optional URL to redirect to after failed submission
};
}
`
$3
Load form schema from a URL:
`tsx
import { SimpleFormRenderer } from "orcha-form-renderer";
schemaUrl="/api/forms/contact"
onSubmitSuccess={(data) => console.log("Success:", data)}
loadingComponent={Loading your form...}
errorComponent={(error) => Error: {error}}
/>;
`
$3
For more control over form behavior:
`tsx
import { useFormRenderer } from "orcha-form-renderer";
function MyForm() {
const {
formData,
submitFormData,
isSubmitting,
resetForm,
lastSubmissionResult,
} = useFormRenderer(formSchema, {
onSubmitSuccess: (data) => console.log("Success:", data),
onSubmitError: (error) => console.log("Error:", error),
onDataChange: (data) => console.log("Data changed:", data),
});
return (
{lastSubmissionResult && (
Last submission: {lastSubmissionResult.success ? "Success" : "Failed"}
)}
);
}
`
Backend Implementation Example
Your backend should implement the webhook proxy endpoints:
`javascript
// Express.js example
app.post("/webhook/proxy", handleWebhookProxy);
app.post("/webhook/proxy/:formName", handleWebhookProxy);
async function handleWebhookProxy(req, res) {
const { webhookUrl, method, data, headers, originalEndpoint, formName } = req.body;
const formNameFromPath = req.params.formName;
const actualFormName = formNameFromPath || formName;
// You can use actualFormName for logging, routing, or processing logic
console.log(Processing form: ${actualFormName || 'unnamed'});
try {
const response = await fetch(webhookUrl, {
method: method,
headers: {
"Content-Type": "application/json",
...headers,
},
body: JSON.stringify({
...data,
_meta: {
formName: actualFormName,
submittedAt: new Date().toISOString(),
}
}),
});
if (response.ok) {
const responseData = await response.text();
res.json({
success: true,
status: response.status,
data: responseData,
formName: actualFormName,
});
} else {
// Optionally try originalEndpoint as fallback
throw new Error(Webhook failed: ${response.status});
}
} catch (error) {
console.error(Webhook proxy error for form ${actualFormName}:, error);
res.status(500).json({
success: false,
error: error.message,
formName: actualFormName,
});
}
}
`
`python
FastAPI example
from fastapi import FastAPI, HTTPException
import httpx
from datetime import datetime
app = FastAPI()
@app.post("/webhook/proxy")
@app.post("/webhook/proxy/{form_name}")
async def webhook_proxy(payload: dict, form_name: str = None):
webhook_url = payload.get("webhookUrl")
method = payload.get("method", "POST")
data = payload.get("data", {})
headers = payload.get("headers", {})
original_endpoint = payload.get("originalEndpoint")
form_name_from_body = payload.get("formName")
# Use form name from path or body
actual_form_name = form_name or form_name_from_body
# Log or process based on form name
print(f"Processing form: {actual_form_name or 'unnamed'}")
try:
# Add metadata to the payload
enhanced_data = {
**data,
"_meta": {
"formName": actual_form_name,
"submittedAt": datetime.now().isoformat()
}
}
async with httpx.AsyncClient() as client:
response = await client.request(
method=method,
url=webhook_url,
json=enhanced_data,
headers={"Content-Type": "application/json", **headers}
)
if response.status_code < 400:
return {
"success": True,
"status": response.status_code,
"data": response.text,
"formName": actual_form_name
}
else:
raise HTTPException(
status_code=500,
detail=f"Webhook failed: {response.status_code}"
)
except Exception as e:
print(f"Webhook proxy error for form {actual_form_name}: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
`
Advanced Usage
$3
Configure automatic redirects after form submission success or failure:
`tsx
const formSchema = {
schema: {
title: "Contact Form",
type: "object",
properties: {
name: { type: "string", title: "Full Name" },
email: { type: "string", title: "Email", format: "email" },
},
required: ["name", "email"],
},
uiSchema: {
email: { "ui:placeholder": "Enter your email" },
},
submission: {
endpoint: "https://api.example.com/contact",
method: "POST",
webhookUrl: "https://webhook.site/your-unique-url",
formName: "contact-form",
redirectOnSuccess: "https://example.com/thank-you",
redirectOnFailed: "https://example.com/error-page",
},
};
`
#### Redirect Behavior
Success Redirects:
- With Webhook: Only redirects if webhook status is 200
- Without Webhook: Redirects normally after successful submission
- Timing: Redirects after success alert closes (or immediately if alerts are disabled)
Error Redirects:
- Always Available: Redirects regardless of webhook configuration
- Timing: Redirects after error alert closes (or immediately if alerts are disabled)
Alert Integration:
`tsx
formSchema={formSchema}
showSuccessAlert={true} // User sees success message, then redirects
showErrorAlert={false} // Redirects immediately on error
/>
`
Use Cases:
- Thank you pages after form completion
- Error pages with additional help or contact information
- Landing pages with next steps or related content
- Analytics tracking pages
$3
`tsx
const formSchema = {
schema: {
/ ... /
},
uiSchema: {
/ ... /
},
submission: {
endpoint: "https://api.example.com/submit",
method: "POST",
headers: {
Authorization: "Bearer your-token",
"X-Custom-Header": "value",
},
transformData: (data) => ({
...data,
timestamp: new Date().toISOString(),
source: "form-renderer",
}),
},
};
`
$3
`tsx
const formSchema = {
schema: {
/ ... /
},
uiSchema: {
/ ... /
},
submission: {
endpoint: "https://api.example.com/fallback", // Fallback URL
webhookUrl: "https://webhook.site/unique-url", // Takes priority
method: "POST",
},
};
`
$3
`tsx
formSchema={formSchema}
showErrorAlert={false} // Disable SweetAlert errors
onSubmitError={(error, data) => {
// Custom error handling
console.error("Submission failed:", error);
// Show your own error UI
showCustomErrorMessage(error.message);
// Maybe save draft data
saveDraftToLocalStorage(data);
}}
/>
`
Integration with Form Builder
This package works seamlessly with the Orcha Form Builder:
1. Design your form using the drag-and-drop builder
2. Export the FormSchema JSON
3. Use FormRenderer to render and handle submissions
`tsx
// Copy FormSchema from drag-and-drop builder
const exportedFormSchema = {
/ ... exported JSON ... /
};
// Render it immediately
;
`
Features
- ✅ JSON Schema Support - Full JSON Schema v7 support
- ✅ Material-UI Integration - Beautiful forms with MUI components
- ✅ SweetAlert2 Integration - User-friendly success/error messages
- ✅ Webhook Support - Direct webhook submission capability
- ✅ TypeScript Support - Full type safety and IntelliSense
- ✅ Customizable - Override styles, messages, and behavior
- ✅ Hook Support - Use as component or hook for flexibility
- ✅ Error Handling - Comprehensive error handling and reporting
- ✅ Data Transformation - Transform data before submission
License
MIT
Contributing
Contributions welcome! Please open an issue or submit a pull request.
Support
For issues and questions, please use the GitHub issues page.
```