Skip to content

Commit 62cf714

Browse files
committed
Initial release — React form engine powered by @formio/js v5
Functional React components for form building and rendering with @formio/js v5, runtime patches for bundler compatibility, and CI workflow for automated npm publishing.
0 parents  commit 62cf714

17 files changed

Lines changed: 3466 additions & 0 deletions

.github/workflows/publish.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Publish to npm
2+
3+
on:
4+
push:
5+
branches: [main]
6+
7+
jobs:
8+
publish:
9+
runs-on: ubuntu-latest
10+
# Only publish when the version in package.json changed
11+
steps:
12+
- uses: actions/checkout@v4
13+
14+
- uses: actions/setup-node@v4
15+
with:
16+
node-version: 22
17+
registry-url: https://registry.npmjs.org
18+
19+
- name: Install dependencies
20+
run: npm ci
21+
22+
- name: Build
23+
run: npm run build
24+
25+
- name: Check if version changed
26+
id: version
27+
run: |
28+
LOCAL=$(node -p "require('./package.json').version")
29+
PUBLISHED=$(npm view react-formio-engine version 2>/dev/null || echo "0.0.0")
30+
echo "local=$LOCAL" >> $GITHUB_OUTPUT
31+
echo "published=$PUBLISHED" >> $GITHUB_OUTPUT
32+
if [ "$LOCAL" != "$PUBLISHED" ]; then
33+
echo "changed=true" >> $GITHUB_OUTPUT
34+
else
35+
echo "changed=false" >> $GITHUB_OUTPUT
36+
fi
37+
38+
- name: Publish to npm
39+
if: steps.version.outputs.changed == 'true'
40+
run: npm publish
41+
env:
42+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
43+
44+
- name: Tag release
45+
if: steps.version.outputs.changed == 'true'
46+
run: |
47+
git config user.name "github-actions"
48+
git config user.email "github-actions@github.com"
49+
git tag "v${{ steps.version.outputs.local }}"
50+
git push origin "v${{ steps.version.outputs.local }}"

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules/
2+
dist/
3+
.DS_Store
4+
*.log

README.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# react-formio-engine
2+
3+
React functional components for form rendering and building, powered by [formiojs](https://github.com/formio/formio.js). Drop-in replacement for `@formio/react` with no class components.
4+
5+
## Install
6+
7+
```bash
8+
npm install react-formio-engine
9+
```
10+
11+
## Quick Start
12+
13+
```jsx
14+
import { Form } from 'react-formio-engine';
15+
import 'bootstrap/dist/css/bootstrap.css';
16+
import 'formiojs/dist/formio.full.css';
17+
18+
const schema = {
19+
display: 'form',
20+
components: [
21+
{ type: 'textfield', key: 'name', label: 'Name', validate: { required: true } },
22+
{ type: 'email', key: 'email', label: 'Email' },
23+
{ type: 'button', action: 'submit', label: 'Submit' },
24+
],
25+
};
26+
27+
function App() {
28+
return <Form src={schema} onSubmit={({ data }) => console.log(data)} />;
29+
}
30+
```
31+
32+
## Exports
33+
34+
| Export | Description |
35+
|--------|-------------|
36+
| `Form` / `FormRenderer` | Renders a form from a JSON schema |
37+
| `FormBuilder` | Drag-and-drop form builder |
38+
| `Formio` | Formio singleton for advanced usage |
39+
| `FormEngineProvider` | Theme context provider |
40+
| `ReactComponent` | Base class for custom components (backward compat) |
41+
| `baseEditForm` | Base settings form for custom component editors |
42+
| `createFormComponent` | Factory for custom components (functional API) |
43+
| `registerComponent` | Register a custom component type |
44+
| `removeSubmitFormio` | Remove submit buttons from a form schema |
45+
| `removeAutoFocusFormio` | Remove autofocus from form components |
46+
47+
## Form Renderer
48+
49+
```jsx
50+
import { Form } from 'react-formio-engine';
51+
52+
<Form
53+
src={formSchema} // Form JSON schema
54+
submission={{ data: { name: 'John' } }} // Initial data
55+
options={{ noAlerts: true }} // Formio options
56+
onSubmit={({ data }) => {}} // Submit callback
57+
onChange={(submission) => {}} // Change callback
58+
ref={formRef} // Access formRef.formio.submit()
59+
/>
60+
```
61+
62+
## Form Builder
63+
64+
```jsx
65+
import { FormBuilder } from 'react-formio-engine';
66+
67+
<FormBuilder
68+
form={{ display: 'form', components: [] }}
69+
options={{
70+
builder: {
71+
basic: false,
72+
customBasic: {
73+
title: 'Fields',
74+
default: true,
75+
components: { textfield: true, email: true, textarea: true, button: true },
76+
},
77+
},
78+
}}
79+
onChange={(schema) => console.log(schema)}
80+
/>
81+
```
82+
83+
## Custom Components
84+
85+
```jsx
86+
import { createFormComponent, Formio } from 'react-formio-engine';
87+
88+
const MyColorPicker = createFormComponent({
89+
type: 'colorpicker',
90+
label: 'Color Picker',
91+
icon: 'paint-brush',
92+
render: ({ value, onChange }) => (
93+
<input type="color" value={value || '#000'} onChange={(e) => onChange(e.target.value)} />
94+
),
95+
});
96+
97+
// Register for use in forms
98+
Formio.registerComponent('colorpicker', MyColorPicker);
99+
```
100+
101+
## Theme Provider
102+
103+
```jsx
104+
import { FormEngineProvider, Form } from 'react-formio-engine';
105+
106+
<FormEngineProvider theme="antd">
107+
<Form src={schema} />
108+
</FormEngineProvider>
109+
```
110+
111+
## Backward Compatibility
112+
113+
- Supports the same JSON schema format as formio.js v4
114+
- `ReactComponent` and `baseEditForm` re-exported for existing custom components
115+
- Drop-in API compatible with `@formio/react`
116+
117+
## Keywords
118+
119+
`react` `formio` `form-builder` `form-renderer` `json-schema-form` `drag-and-drop` `form-engine` `formiojs` `functional-components`
120+
121+
## License
122+
123+
MIT

example/main.jsx

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
import React, { useState, useRef } from 'react';
2+
import { createRoot } from 'react-dom/client';
3+
import { Form, FormBuilder, FormEngineProvider, Formio } from '../src/index';
4+
import 'bootstrap/dist/css/bootstrap.css';
5+
import '../src/themes/default.css';
6+
7+
// Suppress "Missing projectId" warnings — we use formiojs purely client-side
8+
Formio.setProjectUrl(window.location.href);
9+
Formio.setBaseUrl(window.location.href);
10+
11+
// ---------- Sample form schemas ----------
12+
13+
const contactForm = {
14+
display: 'form',
15+
components: [
16+
{
17+
type: 'textfield',
18+
key: 'firstName',
19+
label: 'First Name',
20+
input: true,
21+
validate: { required: true },
22+
},
23+
{
24+
type: 'textfield',
25+
key: 'lastName',
26+
label: 'Last Name',
27+
input: true,
28+
},
29+
{
30+
type: 'email',
31+
key: 'email',
32+
label: 'Email Address',
33+
input: true,
34+
validate: { required: true },
35+
},
36+
{
37+
type: 'phoneNumber',
38+
key: 'phone',
39+
label: 'Phone Number',
40+
input: true,
41+
},
42+
{
43+
type: 'textarea',
44+
key: 'message',
45+
label: 'Message',
46+
input: true,
47+
rows: 4,
48+
},
49+
{
50+
type: 'select',
51+
key: 'category',
52+
label: 'Category',
53+
input: true,
54+
data: {
55+
values: [
56+
{ label: 'General Inquiry', value: 'general' },
57+
{ label: 'Support', value: 'support' },
58+
{ label: 'Sales', value: 'sales' },
59+
],
60+
},
61+
},
62+
{
63+
type: 'checkbox',
64+
key: 'agree',
65+
label: 'I agree to the terms',
66+
input: true,
67+
},
68+
{
69+
type: 'button',
70+
action: 'submit',
71+
label: 'Submit',
72+
theme: 'primary',
73+
},
74+
],
75+
};
76+
77+
// ---------- Demo Components ----------
78+
79+
function FormRendererDemo() {
80+
const [submitted, setSubmitted] = useState(null);
81+
const formRef = useRef(null);
82+
83+
const handleSubmit = (submission) => {
84+
setSubmitted(submission.data);
85+
};
86+
87+
return (
88+
<div style={{ marginBottom: 40 }}>
89+
<h2>Form Renderer</h2>
90+
<p style={{ color: '#666' }}>
91+
Renders a form from a JSON schema. This uses the same schema format as
92+
the old @converselabs/react-formio package.
93+
</p>
94+
<div style={{ border: '1px solid #ddd', padding: 20, borderRadius: 8 }}>
95+
<Form
96+
ref={formRef}
97+
src={contactForm}
98+
options={{ noAlerts: true }}
99+
onSubmit={handleSubmit}
100+
/>
101+
</div>
102+
{submitted && (
103+
<div style={{ marginTop: 16 }}>
104+
<h4>Submitted Data:</h4>
105+
<pre style={{ background: '#f5f5f5', padding: 12, borderRadius: 4 }}>
106+
{JSON.stringify(submitted, null, 2)}
107+
</pre>
108+
</div>
109+
)}
110+
</div>
111+
);
112+
}
113+
114+
function FormRendererWithDataDemo() {
115+
return (
116+
<div style={{ marginBottom: 40 }}>
117+
<h2>Form Renderer with Initial Data</h2>
118+
<p style={{ color: '#666' }}>
119+
Tests the <code>submission</code> prop — pre-populates the form.
120+
</p>
121+
<div style={{ border: '1px solid #ddd', padding: 20, borderRadius: 8 }}>
122+
<Form
123+
src={contactForm}
124+
submission={{
125+
data: {
126+
firstName: 'John',
127+
lastName: 'Doe',
128+
email: 'john@example.com',
129+
category: 'support',
130+
},
131+
}}
132+
options={{ noAlerts: true }}
133+
onSubmit={(sub) => alert('Submitted: ' + JSON.stringify(sub.data))}
134+
/>
135+
</div>
136+
</div>
137+
);
138+
}
139+
140+
function FormBuilderDemo() {
141+
const [schema, setSchema] = useState(null);
142+
143+
return (
144+
<div style={{ marginBottom: 40 }}>
145+
<h2>Form Builder</h2>
146+
<p style={{ color: '#666' }}>
147+
Drag-and-drop form builder. The JSON schema output is shown below.
148+
</p>
149+
<div style={{ border: '1px solid #ddd', padding: 20, borderRadius: 8 }}>
150+
<FormBuilder
151+
form={{ display: 'form', components: [] }}
152+
options={{
153+
builder: {
154+
basic: false,
155+
advanced: false,
156+
premium: false,
157+
data: false,
158+
layout: false,
159+
customBasic: {
160+
title: 'Fields',
161+
default: true,
162+
weight: 1,
163+
components: {
164+
textfield: true,
165+
email: true,
166+
textarea: true,
167+
checkbox: true,
168+
select: true,
169+
radio: true,
170+
number: true,
171+
button: true,
172+
},
173+
},
174+
},
175+
}}
176+
onChange={(form) => setSchema(form)}
177+
/>
178+
</div>
179+
{schema && (
180+
<details style={{ marginTop: 16 }}>
181+
<summary>View JSON Schema Output</summary>
182+
<pre style={{ background: '#f5f5f5', padding: 12, borderRadius: 4, maxHeight: 300, overflow: 'auto' }}>
183+
{JSON.stringify(schema, null, 2)}
184+
</pre>
185+
</details>
186+
)}
187+
</div>
188+
);
189+
}
190+
191+
// ---------- App ----------
192+
193+
function App() {
194+
return (
195+
<FormEngineProvider theme="default">
196+
<div style={{ maxWidth: 800, margin: '0 auto', padding: 20, fontFamily: 'sans-serif' }}>
197+
<h1>react-formio-engine — Demo</h1>
198+
<p>
199+
This demo verifies that FormRenderer, FormBuilder, and the theme
200+
provider work correctly.
201+
</p>
202+
<hr />
203+
<FormRendererDemo />
204+
<FormRendererWithDataDemo />
205+
<FormBuilderDemo />
206+
</div>
207+
</FormEngineProvider>
208+
);
209+
}
210+
211+
createRoot(document.getElementById('root')).render(<App />);

0 commit comments

Comments
 (0)