@unvired/turboforms-embed-sdk
v1.0.20
Published
Reusable vanilla JS form library that works with React, Angular, Ionic, etc.
Downloads
852
Readme
Unvired Forms SDK
A powerful, lightweight JavaScript SDK for rendering dynamic forms with advanced features like geolocation, camera integration, barcode scanning, and file uploads. Built on Form.io JSON schema, this SDK works seamlessly across all modern web frameworks and mobile platforms.
🌟 Features
- Universal Compatibility: Works with React.js, Angular, Ionic, Cordova, and vanilla JavaScript
- Advanced Components: Built-in support for geolocation, camera, barcode scanning, and file attachments
- Form.io Integration: Renders Form.io JSON schema-based forms with full feature support
- Cross-Platform: Seamless integration across web and mobile applications
- Event-Driven: Robust event callback system for form interactions
- TypeScript Support: Full TypeScript definitions included
📦 Installation
npm install unvired-forms-sdk🚀 Usage Examples
Vanilla JavaScript
<!DOCTYPE html>
<html>
<head>
<title>Unvired Forms Integration</title>
</head>
<body>
<div id="form-container"></div>
<script type="module">
import { loadUnviredForms } from './dist/unvired-forms-sdk.js';
const formInstance = loadUnviredForms({
formsData: formTemplateData,
submissionData: preFillData || {},
eventCallback: function (event) {
console.log('Form event:', event);
switch (event.type) {
case 'FORM_SUBMIT':
console.log('Form submitted:', event.data);
break;
case 'FORM_BACK_NAVIGATION':
window.history.back();
break;
}
},
container: document.getElementById('form-container'),
options: {
formioLibPath: {
formioPath: "./dist/assets/formio.full.min.js",
lessFilesPath: "./dist/assets/fomantic-ui/definitions"
},
mode: "render",
platform: "web",
showBackButton: true,
userData: {
user: "John Doe",
email: "[email protected]"
}
}
});
</script>
</body>
</html>React.js Integration
import React, { useRef, useEffect } from 'react';
import { loadUnviredForms } from 'unvired-forms-sdk';
const UnviredForm = ({ formData, onSubmit, onBack }) => {
const containerRef = useRef(null);
const formInstanceRef = useRef(null);
useEffect(() => {
if (containerRef.current && formData) {
formInstanceRef.current = loadUnviredForms({
formsData: formData,
submissionData: {},
eventCallback: (event) => {
switch (event.type) {
case 'FORM_SUBMIT':
onSubmit?.(event.data);
break;
case 'FORM_BACK_NAVIGATION':
onBack?.();
break;
}
},
container: containerRef.current,
options: {
formioLibPath: {
formioPath: "./assets/formio.full.min.js",
lessFilesPath: "./assets/fomantic-ui/definitions"
},
mode: "render",
platform: "web",
showBackButton: true
}
});
}
return () => {
// Cleanup if needed
};
}, [formData, onSubmit, onBack]);
return <div ref={containerRef} style={{ width: '100%', height: '100%' }} />;
};
export default UnviredForm;Angular Integration
import { Component, ElementRef, ViewChild, Input, Output, EventEmitter } from '@angular/core';
import { loadUnviredForms, FormEvent } from 'unvired-forms-sdk';
@Component({
selector: 'app-unvired-form',
template: '<div #formContainer style="width: 100%; height: 100%;"></div>'
})
export class UnviredFormComponent {
@ViewChild('formContainer', { static: true }) formContainer!: ElementRef;
@Input() formData: any;
@Output() formSubmit = new EventEmitter<any>();
@Output() formBack = new EventEmitter<void>();
ngOnInit() {
if (this.formData) {
loadUnviredForms({
formsData: this.formData,
submissionData: {},
eventCallback: (event: FormEvent) => {
switch (event.type) {
case 'FORM_SUBMIT':
this.formSubmit.emit(event.data);
break;
case 'FORM_BACK_NAVIGATION':
this.formBack.emit();
break;
}
},
container: this.formContainer.nativeElement,
options: {
formioLibPath: {
formioPath: "./assets/formio.full.min.js",
lessFilesPath: "./assets/fomantic-ui/definitions"
},
mode: "render",
platform: "web"
}
});
}
}
}Cordova Integration
// In your Cordova app's main JavaScript file
document.addEventListener('deviceready', function() {
import('./js/unvired-forms-sdk.js').then(({ loadUnviredForms }) => {
const formInstance = loadUnviredForms({
formsData: formTemplateData,
submissionData: {},
eventCallback: function (event) {
switch (event.type) {
case 'FORM_SUBMIT':
console.log('Form submitted:', event.data);
break;
case 'OPEN_CAMERA':
handleCameraRequest(event.data);
break;
case 'LOCATION_REQUEST':
handleLocationRequest(event.data);
break;
}
},
container: document.getElementById('form-container'),
options: {
formioLibPath: {
formioPath: "./js/formio.full.min.js",
lessFilesPath: "./css/fomantic-ui/definitions"
},
mode: "render",
platform: "cordova",
showBackButton: true
}
});
});
}, false);📋 API Reference
LoadUnviredFormsParams
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| formsData | string | object | ✅ | Form.io JSON schema or JSON string |
| submissionData | object | ❌ | Pre-filled form data |
| eventCallback | function | ❌ | Event handler function |
| container | HTMLElement | ❌ | DOM container element |
| options | object | ❌ | Configuration options |
Options Configuration
| Property | Type | Description |
|----------|------|-------------|
| formioLibPath | object | FormIO library paths configuration |
| nestedFormData | array | Nested form data |
| masterData | array | Master data for dropdowns |
| title | string | Form title |
| description | string | Form description |
| mode | string | Form mode: 'render', 'readOnly', 'print', 'pdf' |
| platform | string | Platform: 'web', 'android', 'ios', 'cordova' |
| language | string | Language code |
| translations | object | Translation strings |
| themeData | object | Custom theme configuration |
| showBackButton | boolean | Show/hide back navigation |
| showMoreButton | boolean | Show/hide more options menu |
| userData | object | User context data |
| appData | object | Application data |
| environmentVariable | array | Environment variables |
| privateExternal | boolean/string | Controls form visibility logic (Private/Public) |
| permission | string | Access permission ('writesingle', 'writemultiple', 'read') |
| controlData | object | Data for initial control values |
| showLoader | boolean | Show/hide initial SDK loading spinner |
Event Types
| Event | Description |
|-------|-------------|
| FORM_RENDER | Form rendered successfully |
| FORM_SUBMIT | Form submission completed |
| FORM_SAVE | Form data saved |
| FORM_BACK_NAVIGATION | Back button pressed |
| FORM_ONCHANGE | Form field value changed |
| OPEN_CAMERA | Camera access requested |
| LOCATION_REQUEST | Location access requested |
| BARCODE_SCAN | Barcode scanning completed |
| GET_ATTACHMENT | Attachment request |
| ERROR | Error occurred |
✅ Button Visibility & Enablement
| privateExternal | Permission | Main Toolbar Button | More Options (Dropdown) | Enable / Disable Rule |
| ------------------ | --------------- | -------------------------- | ------------------------------- | -------------------------------------------------- |
| false (Public) | writesingle | Submit (submitBtn) | None | Enabled when any field is filled |
| false (Public) | writemultiple | Submit (submitBtn) | None | Enabled when any field is filled |
| true (Private) | writemultiple | Save (saveBtn) | Complete (completeOption) | Save enabled when any field is filled |
| true (Private) | writesingle | Complete (submitBtn) | Save (saveOption) | Complete enabled ONLY when form is 100% filled |
| Any | read | None | None | N/A (Read-only mode) |
🧭 “More Options” Dropdown Logic
▶ Complete appears in More Options
- Condition:
privateExternal = trueANDpermission = writemultiple - Reason: Allows partial saves via main Save button Final submission (Complete) is optional and secondary
▶ Save appears in More Options
- Condition:
privateExternal = trueANDpermission = writesingle - Reason: Forces 100% completion via main Complete button Save is available only as a fallback action
🔑 Core Behavioral Rules
- Public forms → Always Submit, enabled after any input
- Private + writemultiple → Save first, Complete optional
- Private + writesingle → Complete mandatory, Save optional
- Read permission → No actions shown
📂 File Attachment Architecture & Modes
The SDK employs a robust local-first strategy using IndexedDB for offline storage and persistence. To track the exact state of every file or image across its entire lifecycle (from server to local device to edits to deletion), the SDK uniquely assigns a synchronization mode to each file.
Mode Classification
| Mode | Name | Description |
|---|---|---|
| "A" | Added (New) | Represents a brand new file uploaded locally. Newly taken photos, selected files from the gallery, or newly cropped/edited images default to this state. |
| "G" | Got (Downloaded) | Represents a file that previously existed and was downloaded from the server API. The API dictates this mode, and the SDK recognizes it. By default, any file reconstructed from IndexedDB that lacks a mode tag assumes "G". |
| "D" | Deleted | Represents an attachment that was marked as deleted by the user clicking the trash icon. The system physically flags the file in the database rather than removing the row entirely, permitting the host app to notify the server of the deletion upon final sync. |
Working Flow: Cropping or Editing an Image
When a user crops or annotates an existing server image or local image, the SDK executes the following sequence:
- In-Memory Capture: The crop/annotation tool creates a brand new physical
Blobcontaining the updated image metadata safely in RAM. - New DB Entry Created: The SDK assigns a fresh UUID to the newly cropped image and stores it inside the IndexedDB as a completely individual brand new file with
mode: "A". - Original File Deleted: To preserve device storage and state integrity, the SDK locates the original file's UUID corresponding to the un-cropped image and physically deletes it out of the IndexedDB via a
deleteOriginalFileFromDBlistener event. - Memory Re-linking: The form component's memory immediately reflects the presence of the new file object bearing mode
"A"substituted into the form submission pipeline.
This process simplifies the architecture: edits simply generate brand new files, offloading any complex diffing logic to the main application server layer.
⚡ Performance Optimization
Pre-initializing SmartStorage (IndexedDB)
Opening an IndexedDB connection can sometimes introduce a slight delay (lag) during the first file upload or image capture. To eliminate this, the SDK dispatches a smartStorageReady event as soon as the storage provider is registered.
You can listen for this event in your host application and call preInitialize() to "warm up" the database connection immediately during page load, ensuring the first upload is instantaneous.
Implementation Example
// Proper event-driven initialization in your host application
(function() {
const initDb = (SmartStorage) => {
if (SmartStorage && typeof SmartStorage.preInitialize === 'function') {
console.log("⚡ Pre-initializing SmartStorage IndexedDB...");
SmartStorage.preInitialize();
}
};
// 1. Check if already loaded in Formio provider registry
const existing = window.Formio?.providers?.storage?.SmartStorage ||
window.Formio?.Providers?.providers?.storage?.SmartStorage;
if (existing) {
initDb(existing);
} else {
// 2. Otherwise listen for the SDK's "ready" event dispatched on document
document.addEventListener('smartStorageReady', (e) => {
initDb(e.detail.SmartStorage);
}, { once: true });
}
})();🔧 Form Instance Methods
The loadUnviredForms function returns a FormInstance object with the following methods:
const formInstance = loadUnviredForms(params);
// Send actions to the form
formInstance.sendAction({
type: "SET_IMAGE_DATA",
imageData: base64ImageData,
controlId: "photo_field"
});
// Get attachments (if available)
if (formInstance.getAttachments) {
const attachments = await formInstance.getAttachments();
console.log('Form attachments:', attachments);
}
// Clear attachments (if available)
if (formInstance.clearAttachments) {
const success = await formInstance.clearAttachments();
console.log('Attachments cleared:', success);
}
// Validate attachments (if available)
if (formInstance.validateAttachments) {
formInstance.validateAttachments(formData);
}
// Delete specific attachment (if available)
if (formInstance.deleteAttachment) {
const success = await formInstance.deleteAttachment('fileId');
console.log('Attachment deleted:', success);
}Action Types
// Set image data for camera controls
formInstance.sendAction({
type: "SET_IMAGE_DATA",
imageData: "data:image/jpeg;base64,...",
controlId: "camera_field_id"
});
// Set barcode scan result
formInstance.sendAction({
type: "SET_BARCODE_DATA",
scannedValue: "123456789",
controlId: "barcode_field_id"
});
// Set location data
formInstance.sendAction({
type: "SET_LOCATION_DATA",
location: "40.7128,-74.0060",
coordinates: { lat: 40.7128, lng: -74.0060 },
controlId: "location_field_id"
});🛠 Error Handling
The SDK provides comprehensive error handling with specific error codes:
loadUnviredForms({
// ... other params
eventCallback: function(event) {
if (event.type === 'ERROR') {
switch (event.errorMessage) {
case 'Error Code 001 contact your admin or tech team':
console.error('Invalid formsData JSON');
break;
case 'Error Code 004 contact your admin':
console.error('User Data not Provided');
break;
case 'Error Code 013 contact your admin or tech team':
console.error('Attachments missing from storage');
break;
// Handle other error codes...
}
}
}
});Error Codes Reference
| Code | Description | |------|-------------| | 001 | Invalid formsData JSON | | 002 | Nested form validation error | | 003 | Master data validation error | | 004 | User Data not Provided | | 005 | Form render error | | 006 | Form submit error | | 007 | Form complete error | | 008 | Location permission denied | | 009 | Location unavailable | | 010 | Location request timeout | | 011 | Location unknown error | | 012 | Camera access error | | 013 | Attachments missing from storage | | 014 | Failed to validate attachments | | 015 | File not found in IndexedDB |
🔐 Security Considerations
- Validate all form data before processing
- Implement proper file upload restrictions
- Use HTTPS in production environments
- Sanitize user inputs to prevent XSS attacks
🧪 Platform Compatibility
| Platform | Version | Status | |----------|---------|--------| | React.js | 16.8+ | ✅ Fully Supported | | Angular | 12+ | ✅ Fully Supported | | Vue.js | 3.0+ | ✅ Fully Supported | | Ionic | 5+ | ✅ Fully Supported | | Cordova | 9+ | ✅ Fully Supported | | Vanilla JS | ES6+ | ✅ Fully Supported |
🛠 Build Process
Development Setup
Install dependencies
npm installBuild for web platforms
npm run buildBuild for Flutter (optional)
npm run build:flutter
Build Scripts
npm run build- Executesnode scripts/build.jsfor standard web buildnpm run build:flutter- Executesnode scripts/build-flutter.jsfor Flutter-specific build
Build Output
The build process generates:
dist/unvired-forms-sdk.js- Main SDK bundledist/index.d.ts- TypeScript definitionsdist/assets/- FormIO library and UI assets
