Offline Mode Configuration
This guide covers the technical architecture of WittyForm's offline mode, including the PWA service worker, IndexedDB storage, sync protocol, conflict resolution, and how sync status is surfaced on the device. Offline mode requires the Enterprise plan.
PWA service worker
When offline mode is enabled for a form, WittyForm registers a service worker scoped to the form's URL path (/form/[formId]/). The service worker uses a cache-first strategy for static assets and a network-first strategy for the form configuration endpoint.
On the first visit while online, the service worker pre-caches:
- The form HTML shell and all referenced CSS bundles.
- The JavaScript runtime required to render and validate the form.
- The form configuration JSON (field definitions, conditional logic rules, theme data).
- Any static assets referenced by the form (logo images, background images, custom fonts).
After the initial cache is populated, subsequent visits serve from cache immediately and update the cache in the background (stale-while-revalidate for the configuration endpoint). If the network is unavailable, the cached version is served without delay.
The service worker checks for configuration updates every 60 seconds while the form tab is active and online. If an update is detected, the new configuration is cached and applied on the next form load.
IndexedDB storage
Offline data is stored in a single IndexedDB database called WittyFormOfflineDBthat is shared across every form on the device. It holds several tables, including submissions (queued responses), forms (cached form configurations), syncQueue, pendingFiles, and syncLog. Each submission record looks like this:
{
"id": 12,
"formId": "form_abc123",
"responses": {
"field_name": "Jane Doe",
"field_email": "[email protected]",
"field_rating": 5
},
"timestamp": 1726410730000,
"synced": false,
"retryCount": 0,
"idempotencyKey": "form_abc123-1726410730000-a1b2c3"
}The idempotencyKey is generated when the submission is queued. It is what keeps the same response from being saved twice if a sync is retried, so there is no separate device identifier to manage.
Sync protocol
The sync manager runs a periodic poll every 30 seconds while online, and also runs as soon as connectivity is restored. Each pass works through the queue like this:
- The sync manager detects an
onlineevent, or the 30 second poll fires. - All unsynced records in IndexedDB are read from the
submissionstable, ordered bytimestamp(oldest first). - Each submission is sent as a
POSTrequest to/api/forms/[formId]/responseswith the headersX-Idempotency-Key(the record's idempotency key) andX-Submission-Source: offline. - If the request succeeds, the record's
syncedflag is set totrueand it is removed from the pending queue. - If the request fails,
retryCountis incremented and the submission is left in the queue for the next pass. - After
retryCountreaches the maximum of 3 attempts, the record is marked as permanently failed and the form UI prompts the user to check their connection and retry manually.
File uploads attached to an offline submission are uploaded whole through /api/files/upload when the submission syncs, then the stored URLs are written into the response.
Conflict resolution
Offline mode uses a last-write-wins strategy for conflict resolution. Since each offline submission is a unique response (not an edit to an existing response), true conflicts are rare. The main scenario where conflicts can occur is:
- Duplicate detection: If the same submission is sent twice (e.g., due to a network timeout where the server received the data but the client did not receive the acknowledgment), the server uses the submission's
idempotencyKeyto deduplicate. The retried request returns the existing response instead of creating a new one. - Form version mismatch: If the form was updated while the device was offline, submissions made against the old form version are still accepted. The server maps fields by their stable IDs. New fields added since the cached version will be empty in the response. Deleted fields in the submission are preserved as archived data.
Storage limits
IndexedDB storage is controlled by the browser. The following table shows typical limits across major browsers:
| Browser | Storage quota | Notes |
|---|---|---|
| Chrome / Edge | Up to 80% of disk space | Evicted under storage pressure (LRU) |
| Firefox | Up to 50% of disk space (max 2 GB per origin) | Prompts user if exceeded |
| Safari | ~1 GB per origin | Data may be evicted after 7 days of inactivity |
| Safari (Add to Home Screen) | ~1 GB per origin | Persistent storage, not subject to 7-day eviction |
For text-only submissions, each response typically uses 1 to 5 KB of storage. A form can accumulate thousands of offline responses before hitting storage limits. File uploads consume significantly more space: plan accordingly when using offline mode with file upload fields.
Offline-capable vs connectivity-required field types
| Field type | Works offline | Notes |
|---|---|---|
| Text, Email, Number, Phone | Yes | Full functionality including validation |
| Multiple choice, Dropdown, Checkbox | Yes | Options are cached with form config |
| Date, Time | Yes | Browser native pickers work offline |
| Rating, Scale | Yes | Full functionality |
| Long text, Statement | Yes | Full functionality |
| File upload | Partial | Files stored locally, uploaded on reconnect (up to 100 MB on Enterprise) |
| Signature | Yes | Signature canvas works offline, saved as PNG blob |
| Payment (Stripe) | No | Requires active connection to payment processor |
| Address (autocomplete) | Partial | Manual entry works offline only when Address Validation and Google location-type rules are not required |
| Distance / live Maps pricing | No | Requires a live route and signed fare; incompatible Offline mode is publish-blocked |
| reCAPTCHA / Turnstile | No | Challenge verification requires network |
Monitoring sync status
Sync status lives on the device that captured the submissions, not on the server, so there is no remote endpoint to query it. The offline indicator in the form shows how many responses are still pending, and the offline manager UI lets the person on that device watch pending, synced, and failed counts and trigger a manual sync. Once a response syncs, it appears in your dashboard like any other response, so the dashboard is where you confirm everything has come through.
Testing offline mode
To verify that offline mode is working correctly before deploying to the field:
- Enable offline mode for the form and publish it.
- Open the form in Chrome and wait for the service worker to install (check
chrome://serviceworker-internalsor the Application tab in DevTools). - In Chrome DevTools, go to the Network tab and select Offline from the throttling dropdown.
- Reload the page: the form should load from cache with the offline indicator visible.
- Fill out and submit the form. The thank-you page should confirm the response was saved locally.
- Disable the offline throttle. The submission should sync automatically within a few seconds.
- Verify the response appears in your dashboard with the "Offline" tag and the correct original submission timestamp.