Overview
This guide shows you how to integrate Joyfill Components into your React v18 application.🚀 Quick Start
Setup React Project
Create a new React project:Copy
npx create-react-app my-joyfill-app
cd my-joyfill-app
npm install @joyfill/components
Create JoyDoc Component
Createsrc/components/JoyDocForm.js:
Copy
import React, { useState } from 'react';
import { JoyDoc } from '@joyfill/components';
const JoyDocForm = () => {
const [doc, setDoc] = useState(null);
// Complete contact form example
const contactForm = {
"_id": "68f231604484695c16d65f97",
"identifier": "doc_68f231604484695c16d65f97",
"name": "New Doc",
"files": [
{
"_id": "68f231607dee0bbd73994f87",
"name": "New File",
"pageOrder": [
"68f23160b929e32e60e663a0"
],
"pages": [
{
"_id": "68f23160b929e32e60e663a0",
"name": "New Page",
"width": 816,
"height": 1056,
"rowHeight": 8,
"cols": 8,
"fieldPositions": [
{
"_id": "68f23164c57b7fe946d32b1b",
"type": "text",
"displayType": "original",
"x": 0,
"y": 0,
"width": 4,
"height": 8,
"field": "68f23164886cdb084afec912"
},
{
"_id": "68f231663b6a898390b23fec",
"type": "textarea",
"displayType": "original",
"x": 4,
"y": 0,
"width": 4,
"height": 23,
"field": "68f2316612113f6141fac40a"
},
{
"_id": "68f231673dc5e58c883c8437",
"type": "number",
"displayType": "original",
"x": 0,
"y": 8,
"width": 4,
"height": 8,
"field": "68f23167998040ea25a54fa8"
},
{
"_id": "68f231796f5ea38ae37f951b",
"type": "text",
"displayType": "original",
"x": 0,
"y": 16,
"width": 4,
"height": 8,
"field": "68f23179479739cf0883d27e"
}
],
"layout": "grid",
"presentation": "normal",
"padding": 24
}
],
"styles": {
"margin": 4
}
}
],
"fields": [
{
"file": "68f231607dee0bbd73994f87",
"_id": "68f23164886cdb084afec912",
"type": "text",
"title": "Name",
"identifier": "field_68f23164886cdb084afec912"
},
{
"file": "68f231607dee0bbd73994f87",
"_id": "68f2316612113f6141fac40a",
"type": "textarea",
"title": "Comments",
"identifier": "field_68f2316612113f6141fac40a",
"value": ""
},
{
"file": "68f231607dee0bbd73994f87",
"_id": "68f23167998040ea25a54fa8",
"type": "number",
"title": "Phone number",
"identifier": "field_68f23167998040ea25a54fa8"
},
{
"file": "68f231607dee0bbd73994f87",
"_id": "68f23179479739cf0883d27e",
"type": "text",
"title": "Email",
"identifier": "field_68f23179479739cf0883d27e"
}
],
"type": "document"
};
const handleChange = (changelogs, updatedDoc) => {
console.log('Form updated:', updatedDoc);
console.log('Changes:', changelogs);
setDoc(updatedDoc);
};
const handleError = (error) => {
console.error('Form error:', error);
};
return (
<div style={{ padding: '20px' }}>
<h1>Contact Form</h1>
<JoyDoc
doc={contactForm}
mode="edit"
onChange={handleChange}
onError={handleError}
/>
</div>
);
};
export default JoyDocForm;
Update App Component
Updatesrc/App.js:
Copy
import React from 'react';
import JoyDocForm from './components/JoyDocForm';
import './App.css';
function App() {
return (
<div className="App">
<JoyDocForm />
</div>
);
}
export default App;
Start Development Server
Copy
npm start
http://localhost:3000 in your browser.
Complete Project Structure
Copy
my-joyfill-app/
├── src/
│ ├── components/
│ │ └── JoyDocForm.js # JoyDoc component
│ ├── App.js # Main app component
│ ├── App.css # App styles
│ └── index.js # Entry point
├── public/
│ └── index.html # HTML template
└── package.json # Dependencies
Complete React Example
App.js
Copy
import React from 'react';
import JoyDocForm from './components/JoyDocForm';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<h1>🎉 Joyfill Components</h1>
<p>React v18 Integration Example</p>
</header>
<main>
<JoyDocForm />
</main>
</div>
);
}
export default App;
App.css
Copy
.App {
text-align: center;
min-height: 100vh;
background-color: #f5f5f5;
}
.App-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
color: white;
margin-bottom: 20px;
}
.App-header h1 {
margin: 0;
font-size: 2rem;
}
.App-header p {
margin: 10px 0 0 0;
opacity: 0.9;
}
main {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
JoyDocForm.js
Copy
import React, { useState, useCallback } from 'react';
import { JoyDoc } from '@joyfill/components';
const JoyDocForm = () => {
const [doc, setDoc] = useState(null);
const [isLoading, setIsLoading] = useState(false);
// Complete contact form example
const contactForm = {
"_id": "68f231604484695c16d65f97",
"identifier": "doc_68f231604484695c16d65f97",
"name": "New Doc",
"files": [
{
"_id": "68f231607dee0bbd73994f87",
"name": "New File",
"pageOrder": [
"68f23160b929e32e60e663a0"
],
"pages": [
{
"_id": "68f23160b929e32e60e663a0",
"name": "New Page",
"width": 816,
"height": 1056,
"rowHeight": 8,
"cols": 8,
"fieldPositions": [
{
"_id": "68f23164c57b7fe946d32b1b",
"type": "text",
"displayType": "original",
"x": 0,
"y": 0,
"width": 4,
"height": 8,
"field": "68f23164886cdb084afec912"
},
{
"_id": "68f231663b6a898390b23fec",
"type": "textarea",
"displayType": "original",
"x": 4,
"y": 0,
"width": 4,
"height": 23,
"field": "68f2316612113f6141fac40a"
},
{
"_id": "68f231673dc5e58c883c8437",
"type": "number",
"displayType": "original",
"x": 0,
"y": 8,
"width": 4,
"height": 8,
"field": "68f23167998040ea25a54fa8"
},
{
"_id": "68f231796f5ea38ae37f951b",
"type": "text",
"displayType": "original",
"x": 0,
"y": 16,
"width": 4,
"height": 8,
"field": "68f23179479739cf0883d27e"
}
],
"layout": "grid",
"presentation": "normal",
"padding": 24
}
],
"styles": {
"margin": 4
}
}
],
"fields": [
{
"file": "68f231607dee0bbd73994f87",
"_id": "68f23164886cdb084afec912",
"type": "text",
"title": "Name",
"identifier": "field_68f23164886cdb084afec912"
},
{
"file": "68f231607dee0bbd73994f87",
"_id": "68f2316612113f6141fac40a",
"type": "textarea",
"title": "Comments",
"identifier": "field_68f2316612113f6141fac40a",
"value": ""
},
{
"file": "68f231607dee0bbd73994f87",
"_id": "68f23167998040ea25a54fa8",
"type": "number",
"title": "Phone number",
"identifier": "field_68f23167998040ea25a54fa8"
},
{
"file": "68f231607dee0bbd73994f87",
"_id": "68f23179479739cf0883d27e",
"type": "text",
"title": "Email",
"identifier": "field_68f23179479739cf0883d27e"
}
],
"type": "document"
};
const handleChange = useCallback((changelogs, updatedDoc) => {
console.log('Form updated:', updatedDoc);
console.log('Changes:', changelogs);
setDoc(updatedDoc);
}, []);
const handleError = useCallback((error) => {
console.error('Form error:', error);
}, []);
const handleFocus = useCallback((fieldId, fieldData) => {
console.log('Field focused:', fieldId);
}, []);
const handleBlur = useCallback((fieldId, fieldData) => {
console.log('Field blurred:', fieldId);
}, []);
const saveForm = async () => {
if (!doc) return;
setIsLoading(true);
try {
const response = await fetch('/api/contact', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(doc)
});
if (response.ok) {
console.log('Form saved successfully');
alert('Form saved successfully!');
} else {
console.error('Failed to save form');
alert('Failed to save form');
}
} catch (error) {
console.error('Error saving form:', error);
alert('Error saving form');
} finally {
setIsLoading(false);
}
};
return (
<div style={{
background: 'white',
borderRadius: '8px',
padding: '30px',
boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
marginBottom: '20px'
}}>
<h2 style={{ color: '#333', marginBottom: '20px' }}>Contact Us</h2>
<JoyDoc
doc={contactForm}
mode="edit"
onChange={handleChange}
onError={handleError}
onFocus={handleFocus}
onBlur={handleBlur}
width={800}
height={600}
/>
<div style={{ marginTop: '20px', textAlign: 'center' }}>
<button
onClick={saveForm}
disabled={isLoading}
style={{
background: '#007bff',
color: 'white',
border: 'none',
padding: '12px 24px',
borderRadius: '4px',
cursor: isLoading ? 'not-allowed' : 'pointer',
fontSize: '16px',
opacity: isLoading ? 0.6 : 1
}}
>
{isLoading ? 'Saving...' : 'Save Form'}
</button>
</div>
</div>
);
};
export default JoyDocForm;
Advanced React Integration
Custom Hook for Form Management
Createsrc/hooks/useJoyDocForm.js:
Copy
import { useState, useCallback } from 'react';
export const useJoyDocForm = (initialDoc) => {
const [doc, setDoc] = useState(initialDoc);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const handleChange = useCallback((changelogs, updatedDoc) => {
setDoc(updatedDoc);
setError(null);
}, []);
const handleError = useCallback((error) => {
setError(error);
}, []);
const saveForm = useCallback(async (saveUrl) => {
if (!doc) return;
setIsLoading(true);
setError(null);
try {
const response = await fetch(saveUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(doc)
});
if (!response.ok) {
throw new Error('Failed to save form');
}
return await response.json();
} catch (error) {
setError(error);
throw error;
} finally {
setIsLoading(false);
}
}, [doc]);
return {
doc,
isLoading,
error,
handleChange,
handleError,
saveForm
};
};
Using the Custom Hook
Copy
import React from 'react';
import { JoyDoc } from '@joyfill/components';
import { useJoyDocForm } from '../hooks/useJoyDocForm';
const ContactForm = () => {
const contactForm = {
// ... your form data
};
const {
doc,
isLoading,
error,
handleChange,
handleError,
saveForm
} = useJoyDocForm(contactForm);
const handleSave = async () => {
try {
await saveForm('/api/contact');
alert('Form saved successfully!');
} catch (error) {
alert('Error saving form');
}
};
return (
<div>
{error && (
<div style={{ color: 'red', marginBottom: '10px' }}>
Error: {error.message}
</div>
)}
<JoyDoc
doc={doc}
mode="edit"
onChange={handleChange}
onError={handleError}
/>
<button onClick={handleSave} disabled={isLoading}>
{isLoading ? 'Saving...' : 'Save Form'}
</button>
</div>
);
};
export default ContactForm;
Common Configuration Options
Copy
<JoyDoc
doc={yourFormData} // Your form JSON
mode="edit" // 'edit' | 'fill' | 'readonly'
view="desktop" // 'desktop' | 'mobile' | 'tablet'
width={800} // Component width
height={600} // Component height
theme={{ // Custom theme
primaryColor: '#007bff',
secondaryColor: '#6c757d'
}}
features={{ // Feature flags
formulas: true, // formulas
readableIds: false, // readableIds
validateSchema: false // schemaValidations
}}
onChange={(changes, doc) => {
// Handle form changes
}}
onError={(error) => {
// Handle errors
}}
onFocus={(fieldId, fieldData) => {
// Handle field focus
}}
onBlur={(fieldId, fieldData) => {
// Handle field blur
}}
onCaptureAsync={async (fieldId, data) => {
// Handle field capture
return data;
}}
onUploadAsync={async (file) => {
// Handle file upload
return { url: 'uploaded-file-url' };
}}
/>