Bungkus: A lightweight React Hook to save form data (files & text) to IndexedDB. Prevent data loss on refresh.
npm install bungkus




Form persistence for the paranoid
Saves form data (including Files/Blobs) to IndexedDB with encryption and auto-expiration
---
Your user spends 20 minutes filling out a registration form. They attach a photo of their KTP. Then, they accidentally hit "Refresh" or their WiFi dies. The form resets. The user rage-quits.
bungkus fixes this by silently saving every keystroke and file upload to a secure local database. When the user comes back, the data is restored instantly.
Because localStorage is weak. It chokes on files (images/PDFs), it's unencrypted (anyone inspecting the browser can read it), and it stays there forever until you manually clear it.
We provide File Support, Encryption, and Auto-Expiry. All in one tiny hook.
| Feature | localStorage | bungkus |
| :--- | :--- | :--- |
| Saves Text | ✅ Yes | ✅ Yes |
| Saves Files (Images/PDF) | ❌ No (String only) | ✅ Yes (Blobs) |
| Encryption | ❌ Naked Text | ✅ Obfuscated |
| Auto-Expiry (TTL) | ❌ Manual only | ✅ Automatic |
---
``bash`
npm install bungkus
---
Want to see the magic before installing? We included a full playground in this repo.
`bash1. Clone the repo
git clone https://github.com/nafhansa/bungkus.git
Try filling out the form, uploading a file, and refreshing the browser. The data persists!
> ⚠️ Note on File Inputs: Due to browser security, we cannot programmatically restore the value of
. The file input will look empty after refresh, but the File/Blob data is safely stored.
>
> You should use a visual preview (like an tag) to show the user that their file is still there. (See src/App.tsx for the implementation example).---
Usage
$3
Perfect for long forms. It watches your inputs and saves them. If the browser crashes, the data survives.
`typescript
import { useBungkus } from 'bungkus';function RegistrationForm() {
const { daftarkan, buangBungkus } = useBungkus('register-v1');
return (
);
}
`$3
For when you don't want curious eyes reading sensitive data in the DevTools > Application tab. Text data is obfuscated before hitting the database.
`typescript
const { daftarkan } = useBungkus('secret-mission', {
rahasia: true
});
`$3
Don't let old data rot in the user's browser. Set a timer. If the user comes back after the time limit, the data is treated as "expired" and purged automatically.
`typescript
const { values } = useBungkus('short-lived-form', {
kadaluarsa: 1
});
`---
Advanced Features
$3
Synchronize form data across multiple browser tabs in real-time using BroadcastChannel API. Perfect for multi-tab workflows.
`typescript
import { useBungkusLive } from 'bungkus/toolkit/useBungkusLive';function MultiTabForm() {
const { daftarkan, buangBungkus } = useBungkusLive('my-form');
return (
);
}
`Features:
- Automatic synchronization across browser tabs
- Prevents echo/loop with smart update detection
- Uses the same API as
useBungkus---
$3
Manage complex multi-step forms with isolated storage per step. Each step gets its own storage space.
`typescript
import { useBungkusWizard } from 'bungkus/toolkit/useBungkusWizard';function RegistrationWizard() {
const [currentStep, setCurrentStep] = useState(1);
const { daftarkan, wizardInfo } = useBungkusWizard('registration', currentStep);
return (
{currentStep === 1 && (
)}
{currentStep === 2 && (
)}
{/ Each step is saved independently /}
);
}
`Features:
- Unique storage per step
- Navigate between steps without data loss
- Access to
wizardInfo for current step tracking---
$3
Automatically sync form data to your backend when the user comes back online. Perfect for offline-first Progressive Web Apps (PWAs).
`typescript
import { useBungkus } from 'bungkus';
import { useBungkusSync } from 'bungkus/toolkit/useBungkusSync';// Your API function
const submitToServer = async (data) => {
const response = await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify(data)
});
return response.json();
};
function OfflineForm() {
const { daftarkan } = useBungkus('offline-form-v1');
const { networkStatus, triggerSync, hasPendingData } = useBungkusSync(
'offline-form-v1', // Must match useBungkus formId
submitToServer, // Your API function
{
autoSync: true, // Auto-upload when online
onSuccess: (res) => alert('✅ Submitted!'),
onError: (err) => alert('❌ Failed to submit')
}
);
return (
);
}
`Features:
- Real-time network status detection (online/offline/syncing/error)
- Auto-upload when internet reconnects
- Manual trigger support
- Success/Error callbacks
- Smart polling (500ms) for reliable offline detection
---
$3
A floating DevTools panel for debugging and inspecting your form's IndexedDB storage in real-time during development. Perfect for debugging and monitoring what's being saved.
`typescript
import { BungkusDevTools } from 'bungkus/toolkit/BungkusDevTools';function App() {
return (
<>
{/ Add DevTools - only shows in development /}
{process.env.NODE_ENV === 'development' && (
formId="your-form-id"
position="bottom-right"
refreshInterval={500}
/>
)}
>
);
}
`Features:
- 🌿 Beautiful forest-themed UI with glassmorphism
- 📊 Real-time data updates (500ms polling by default)
- 💾 Live storage size monitoring
- 📦 Pretty JSON viewer with Blob detection
- 🗑️ Quick storage clear button
- 🎯 Floating button that doesn't interfere with your UI
Props:
-
formId (required): The form ID to inspect (must match your useBungkus formId)
- position (optional): 'bottom-right' or 'bottom-left' (default: 'bottom-right')
- refreshInterval (optional): Milliseconds between data refreshes (default: 500)Best Practice: Only include BungkusDevTools in development mode to avoid shipping it to production.
---
Cheat Sheet (Docs)
Look, I built this because I needed to save files in a form without a backend. Here is the quick API:
$3
Parameters:
-
formId (required): Unique string to identify the form.
- config.kadaluarsa (default: 24): Hours before data expires.
- config.rahasia (default: true): Encrypt text data.
- config.compress (default: false): Compress images before saving.Returns:
-
daftarkan(name): Helper to spread into . Handles value, onChange, and files.
- buangBungkus(): Call this on successful submit to clear the storage.
- values: The raw data object (if you need it manually).
- status: 'idle' | 'memulihkan' | 'siap'.$3
Same as
useBungkus but with cross-tab synchronization. Use when you need real-time sync across browser tabs.$3
Parameters:
-
wizardName (required): Unique name for the entire wizard flow.
- step (required): Current step number.Returns:
- All
useBungkus returns
- wizardInfo: Object with currentStep and storageKey$3
Parameters:
-
formId (required): Must match the formId used in useBungkus.
- apiCall (required): Async function that takes form data and returns a Promise. This is your backend API call.
- config.autoSync (default: true): Automatically upload when internet reconnects.
- config.onSuccess: Callback function called after successful upload.
- config.onError: Callback function called on upload failure.Returns:
-
networkStatus: Current network state ('online' | 'offline' | 'syncing' | 'error')
- triggerSync(): Manual function to trigger upload
- hasPendingData: Boolean indicating if there's data waiting to be synced$3
Props:
-
formId (required): The form ID to inspect
- position (optional): 'bottom-right' | 'bottom-left'
- refreshInterval (optional): Refresh rate in milliseconds (default: 500`)Component: Floating visual inspector for debugging IndexedDB storage in real-time.
---
MIT