Your Own App
Form.io enables you to build your own online and offline Web App with easy "drag and drop".
Your web app works across ALL devices e.g. iOS and Android phones, Windows and macOS laptops etc. ... even on most smart TVs!
Form.io enables you to build your own online and offline Web App with easy "drag and drop".
Your web app works across ALL devices e.g. iOS and Android phones, Windows and macOS laptops etc. ... even on most smart TVs!
We can use the Text Field component with "custom logic" to populate a regular Select Box dynamically.
Add a Text Field Component:
[]
.Add a Select Box Component:
{ value: '', label: 'Loading...' }
).Custom Logic to Populate Data:
Basic custom logic code:
const apiUrl = 'https://api.example.com/getOptions';
fetch(apiUrl)
.then(response => response.json())
.then(data => {
if (Array.isArray(data.options)) {
const options = data.options.map(item => ({
label: item.label,
value: item.id
}));
// Save the options in the Text Field
data.value = options;
// Update the Select Box
const selectBox = form.getComponent('dynamicSelect');
if (selectBox) {
selectBox.component.data.values = options;
selectBox.redraw();
}
}
})
.catch(err => console.error('Error fetching options:', err));
Explanation
data.value
stores the fetched options..redraw()
.Tips
true
for the Text Field.Considerations
label
and value
).fetch
request to include necessary headers.Assuming the response from the URL is:
{
"data": [
{ "id": "1", "label": "Option 1" },
{ "id": "2", "label": "Option 2" },
{ "id": "3", "label": "Option 3" }
]
}
[
{ "value": "1", "label": "Option 1" },
{ "value": "2", "label": "Option 2" },
{ "value": "3", "label": "Option 3" }
]
Data Fetcher
dataFetcher
true
(Check the Hidden property)Label: Dynamic Select
Key: dynamicSelect
Data Source Type: Values
Add a single placeholder value:
{ value: '', label: 'Loading...' }
dataFetcher
).Custom Logic Code
Custom logic code with timeout and error handling:
const apiUrl = 'https://api.example.com/getOptions';
const timeoutDuration = 5000; // 5 seconds
const selectBox = form.getComponent('dynamicSelect');
// Show a loading indicator
selectBox.component.data.values = [{ value: '', label: 'Loading...' }];
selectBox.redraw();
// Fetch data with timeout
const fetchData = new Promise((resolve, reject) => {
const timeout = setTimeout(() => reject(new Error('Request timed out')), timeoutDuration);
fetch(apiUrl)
.then(response => response.json())
.then(data => {
clearTimeout(timeout);
resolve(data);
})
.catch(reject);
});
fetchData
.then(data => {
if (Array.isArray(data.options)) {
const options = data.options.map(item => ({
label: item.label,
value: item.id
}));
selectBox.component.data.values = options;
} else {
throw new Error('Invalid data format');
}
})
.catch(err => {
console.error(err.message);
selectBox.component.data.values = [{ value: '', label: 'Error loading data' }];
})
.finally(() => {
selectBox.redraw();
});
Explanation:
'https://api.example.com/getOptions'
with the actual API endpoint.{
"options": [
{ "id": "1", "label": "Option 1" },
{ "id": "2", "label": "Option 2" }
]
}
dataFetcher
) and simultaneously used to update the Select Box.true
for the dataFetcher
component.Progressive Web Application (PWA) provide extract capabilities (e.g. offline operation) not available in traditional web apps.
Although form.io supports a lot of external javascript frameworks:
It is possible to build a PWA with offline ability:
The following is a simple example of a PWA using form.io only.
formio/
├── app/ ← Form.io backend logic
├── public/ ← PWA goes here
│ ├── index.html
│ ├── service-worker.js
│ ├── manifest.json
│ └── icon.png
├── server.js ← Main Entry Point
├── .env ← Set MONGO_URI, JWT_SECRET, etc.
└── package.json
To run
npm install
node server.js
PWA now available at http://localhost:3001/index.html
and the forms are available http://localhost:3001/form/:formName
const express = require('express');
const path = require('path');
const app = express();
// 1. Serve static frontend files (PWA)
app.use(express.static(path.join(__dirname, 'public')));
// 2. Load Form.io backend
require('./app')(app); // Loads Form.io routes, middleware, etc.
// 3. Fallback to index.html for unknown frontend routes (SPA support)
app.get('*', (req, res, next) => {
if (req.path.startsWith('/form') || req.path.startsWith('/user') || req.path.startsWith('/submission')) {
// Let Form.io handle its own API routes
return next();
}
res.sendFile(path.join(__dirname, 'public/index.html'));
});
// 4. Start the server
const port = process.env.PORT || 3001;
app.listen(port, () => {
console.log(` Form.io server with PWA running at http://localhost:${port}`);
});
public/
folder should contain index.html
, service-worker.js
, manifest.json
, etc.require('./app')(app)
— this is the core Form.io API server.index.html
for all non-API routes.if
condition if using custom Express routes (e.g., /auth
, /api
, etc.)<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Form.io PWA</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#0074D9">
<link rel="manifest" href="manifest.json">
<link rel="icon" href="icon.png" type="image/png">
<script src="https://cdn.form.io/formiojs/formio.full.min.js"></script>
<style>
body {
font-family: sans-serif;
margin: 1rem;
}
</style>
</head>
<body>
<h1>Form.io Offline PWA</h1>
<div id="formio">Loading form...</div>
<script>
// Render form from your self-hosted Form.io backend
Formio.createForm(document.getElementById('formio'), 'http://localhost:3001/form/myform', {
offline: true,
saveDraft: true
}).then(form => {
form.on('submit', (submission) => {
alert('Form submitted!');
console.log(submission);
});
});
// Register service worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js')
.then(() => console.log(' Service Worker registered!'))
.catch(err => console.error(' Service Worker registration failed:', err));
}
</script>
</body>
</html>
{
"name": "Form.io PWA",
"short_name": "FormApp",
"start_url": "/index.html",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#0074D9",
"icons": [
{
"src": "icon.png",
"sizes": "192x192",
"type": "image/png"
}
]
}
const cacheName = 'formio-pwa-cache-v1';
const filesToCache = [
'/',
'/index.html',
'/manifest.json',
'/icon.png',
'https://cdn.form.io/formiojs/formio.full.min.js'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(cacheName).then((cache) => cache.addAll(filesToCache))
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then(response => response || fetch(event.request))
);
});