
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
Dynamic form renderer component based on JSON Schema with drag-and-drop form builder
A powerful React component library for rendering dynamic forms based on JSON Schema with built-in submission handling, webhook proxy support, and a drag-and-drop form builder.
npm install fenderer
Make sure you have these installed:
npm install react react-dom @mui/material @mui/icons-material @emotion/react @emotion/styled
Before using any form components, you must configure the library with your backend URL:
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
});
When a form has a webhookUrl configured:
With webhookUrl: Form data is sent to:
${backendUrl}/webhook/proxy/{formName} (if formName is provided)${backendUrl}/webhook/proxy (if no formName)With payload:
{
"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
}
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.
import { FormRenderer, configureOrchaForm } from "fenderer";
// Configure first
configureOrchaForm({
backendUrl: "https://your-backend.com/api",
});
## Components
### FormBuilder - Drag & Drop Form Builder
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 (
<FormBuilder
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",
},
};
<FormBuilder
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`
### FormRenderer
Main form component for rendering JSON Schema forms:
```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 (
<FormRenderer
formSchema={formSchema}
onSubmitSuccess={(data) => console.log("Success:", data)}
onSubmitError={(error) => console.log("Error:", error)}
/>
);
}
```
## API Reference
### FormRenderer Props
- `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
### FormSchema Structure
```tsx
interface FormSchema {
schema: JSONSchema7; // JSON Schema for form validation
uiSchema: Record<string, any>; // 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<string, string>;
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
};
}
```
### SimpleFormRenderer
Load form schema from a URL:
```tsx
import { SimpleFormRenderer } from "orcha-form-renderer";
<SimpleFormRenderer
schemaUrl="/api/forms/contact"
onSubmitSuccess={(data) => console.log("Success:", data)}
loadingComponent={<div>Loading your form...</div>}
errorComponent={(error) => <div>Error: {error}</div>}
/>;
```
### useFormRenderer Hook
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 (
<div>
<button onClick={() => submitFormData()} disabled={isSubmitting}>
{isSubmitting ? "Submitting..." : "Submit Form"}
</button>
<button onClick={resetForm}>Reset Form</button>
{lastSubmissionResult && (
<div>
Last submission: {lastSubmissionResult.success ? "Success" : "Failed"}
</div>
)}
</div>
);
}
```
## 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
### Form Redirections
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
<FormRenderer
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
### Custom Submission Logic
```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",
}),
},
};
```
### Webhook Integration
```tsx
const formSchema = {
schema: {
/* ... */
},
uiSchema: {
/* ... */
},
submission: {
endpoint: "https://api.example.com/fallback", // Fallback URL
webhookUrl: "https://webhook.site/unique-url", // Takes priority
method: "POST",
},
};
```
### Error Handling
```tsx
<FormRenderer
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
<FormRenderer formSchema={exportedFormSchema} />;
```
## 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.
FAQs
Dynamic form renderer component based on JSON Schema with drag-and-drop form builder
The npm package fenderer receives a total of 9 weekly downloads. As such, fenderer popularity was classified as not popular.
We found that fenderer demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.