@proveanything/smartlinks-form-renderer
v0.2.0
Published
SmartLinks form renderer and editor — render forms from JSON schema or build them with the visual editor
Downloads
95
Maintainers
Readme
SmartLinks Forms
A flexible React package for rendering and editing forms from JSON schema — with conditional logic, validation, table fields, storage configuration, and a visual editor.
Installation
npm install @proveanything/smartlinks-formsSubpath Imports
// Full package (renderer + editor + types)
import { SchemaFormRenderer, useFormEditorState } from '@proveanything/smartlinks-forms';
// Renderer only (lean, public-facing)
import { SchemaFormRenderer } from '@proveanything/smartlinks-forms/renderer';
// Editor only (admin-facing)
import { useFormEditorState, useFormEditorActions } from '@proveanything/smartlinks-forms/editor';Form Renderer
Basic Usage
import { SchemaFormRenderer, SchemaFormConfig } from '@proveanything/smartlinks-forms';
const formConfig: SchemaFormConfig = {
title: "Contact Form",
description: "Fill in your details",
schema: {
type: "object",
properties: {
name: { type: "string", title: "Full Name" },
email: { type: "string", format: "email", title: "Email" },
},
required: ["name", "email"]
},
uiSchema: {
name: { "ui:placeholder": "Enter your full name" },
email: { "ui:placeholder": "[email protected]" },
},
storage: { name: "public", email: "private" },
settings: {
allowMultipleSubmissions: false,
requireAuthentication: false,
showProgressBar: false,
submitButtonText: "Submit",
successMessage: "Form submitted successfully!"
},
styling: { theme: "default", primaryColor: "#007bff", backgroundColor: "#ffffff" },
fieldOrder: ["name", "email"]
};
function MyForm() {
return (
<SchemaFormRenderer
config={formConfig}
onSubmit={(data) => console.log('Submitted:', data)}
initialData={{ name: "John Doe" }}
/>
);
}Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| config | SchemaFormConfig | Required | The form configuration object |
| onSubmit | (data: Record<string, any>) => void | Required | Called on valid submission |
| isSubmitting | boolean | false | Disables submit button |
| className | string | "" | Additional CSS classes |
| initialData | Record<string, any> | {} | Pre-populate fields (overrides schema defaults) |
| components | object | {} | Custom component overrides |
Field Types & Formats
String Fields
text(default),email,textarea,select,radio,checkboxes,multiselect,combobox,file,date,date-time
Boolean Fields
checkbox(default),switch
Number / Integer Fields
- Standard number input with
minimum/maximumvalidation
Table Fields
- Dynamic table with typed columns, row add/remove,
minRows/maxRows
Validation
The renderer enforces validation at submit time with inline error messages. Both HTML attributes and JavaScript-level checks are used.
Supported Constraints
| Constraint | Applies To | Description |
|------------|-----------|-------------|
| required | All types | Field must have a value (set via schema.required[]) |
| minLength | string | Minimum character length |
| maxLength | string | Maximum character length |
| pattern | string | Regex pattern the value must match |
| minimum | number, integer | Minimum numeric value |
| maximum | number, integer | Maximum numeric value |
| minRows | table | Minimum number of table rows |
| maxRows | table | Maximum number of table rows |
Example
{
age: {
type: "integer",
title: "Age",
minimum: 18,
maximum: 120
},
bio: {
type: "string",
format: "textarea",
title: "Bio",
minLength: 10,
maxLength: 500
}
}Enum Labels (enumNames)
Use enumNames as a parallel array to enum for human-readable labels:
{
country: {
type: "string",
format: "select",
title: "Country",
enum: ["us", "gb", "de"],
enumNames: ["United States", "United Kingdom", "Germany"]
}
}enum[i] is the stored value, enumNames[i] is the display label. Works with select, radio, checkboxes, multiselect, and combobox formats.
UI Schema
Control presentation without changing the data schema:
uiSchema: {
bio: {
"ui:placeholder": "Tell us about yourself...",
"ui:help": "This will be shown on your profile",
"ui:options": { rows: 6 },
"ui:disabled": true // Read-only field
},
avatar: {
"ui:options": { accept: "image/*" }
}
}Available UI Properties
| Property | Type | Description |
|----------|------|-------------|
| ui:placeholder | string | Placeholder text |
| ui:help | string | Help text below the field |
| ui:disabled | boolean | Disables the field (read-only) |
| ui:widget | string | Override auto-detected widget |
| ui:options.rows | number | Textarea row count |
| ui:options.accept | string | File input accept filter |
| ui:options.label | boolean | Show/hide label |
Conditional Logic
Show/hide fields based on other field values:
{
preferences: {
type: "string",
format: "select",
title: "Preferences",
enum: ["daily", "weekly"],
conditions: [
{ targetFieldId: "subscribe", operator: "equals", value: true }
],
showWhen: "all" // or "any"
}
}Operators
| Operator | Description | Needs value? |
|----------|-------------|---------------|
| equals | Strict equality | Yes |
| not_equals | Not equal | Yes |
| contains | String contains | Yes |
| not_contains | String doesn't contain | Yes |
| greater_than | Numeric comparison | Yes |
| less_than | Numeric comparison | Yes |
| is_empty | Value is empty/null | No |
| is_not_empty | Value has content | No |
| is_true | Value is true | No |
| is_false | Value is false/null/undefined | No |
Field Ordering
const config: SchemaFormConfig = {
fieldOrder: ["name", "email", "phone", "address"],
// ...
};If fieldOrder is not specified, fields appear in schema.properties insertion order.
Table Fields
{
items: {
type: "table",
title: "Line Items",
columns: [
{ id: "name", title: "Item", type: "string", required: true },
{ id: "qty", title: "Quantity", type: "number", required: true },
{ id: "active", title: "Active", type: "boolean" },
{ id: "date", title: "Added", type: "date" }
],
minRows: 1,
maxRows: 10
}
}Custom Components
Override default UI primitives:
<SchemaFormRenderer
config={formConfig}
onSubmit={handleSubmit}
components={{
Button: MyButton,
Input: MyInput,
Textarea: MyTextarea,
Select: MySelect,
Checkbox: MyCheckbox,
RadioGroup: MyRadioGroup,
Switch: MySwitch,
Label: MyLabel,
TableInput: MyTableInput,
}}
/>Storage Configuration
Define privacy levels per field:
storage: {
name: "public", // Publicly visible
email: "private", // Private to user
ssn: "proof", // Cryptographic proof only
notes: "personal" // Personal notes
}Form Editor (Admin)
The editor hooks provide headless state management for building a form editor UI:
import { useFormEditorState, useFormEditorActions } from '@proveanything/smartlinks-forms/editor';
function FormEditor() {
const editor = useFormEditorState({ isCreating: true });
const actions = useFormEditorActions({
formConfig: editor.formConfig,
setFormConfig: editor.setFormConfig,
});
// editor.addField(), editor.updateField(), editor.removeField(), etc.
// actions.exportSchema(), actions.importSchema()
}Configuration Reference
SchemaFormConfig
| Property | Type | Description |
|----------|------|-------------|
| id | string? | Unique identifier |
| title | string | Form title |
| description | string | Form description |
| schema | object | JSON Schema definition |
| uiSchema | object | UI configuration |
| storage | object | Per-field storage levels |
| settings | object | Form behavior settings |
| styling | object | Theme and colours |
| fieldOrder | string[]? | Field display order |
TypeScript
import type {
SchemaFormConfig,
SchemaFormField,
SchemaFormRendererProps,
SchemaFormUIConfig,
SchemaFormFieldCondition,
TableColumn,
FormEditorState,
FormEditorActions,
} from '@proveanything/smartlinks-forms';License
MIT
