React Hooks
The Construct SDK provides React hooks for data persistence, PWA features, and more.
import {
useConstructData,
useDataStore,
useOnlineStatus,
usePWAInstall,
useIsInstalledPWA,
useImportExport,
useInterAppComm,
useRuntime,
} from "@useworkapp/construct-sdk";useConstructData
The primary hook for persisting data with IndexedDB and optional cloud sync.
useConstructData<T>(key: string, defaultValue: T, options?: Options)
// Basic usage
const [todos, setTodos] = useConstructData<Todo[]>("todos", []);
// With cloud sync
const [data, setData, { status, isLoading, sync }] = useConstructData(
"my-data",
defaultValue,
{ sync: true }
);
// Options
interface Options {
sync?: boolean; // Enable cloud sync (default: false)
syncDebounce?: number; // Debounce sync in ms (default: 1000)
}
// Return metadata
{
status: 'local' | 'syncing' | 'synced' | 'offline' | 'error';
isLoading: boolean;
error: Error | null;
sync: () => Promise<void>; // Manual sync trigger
version: number; // Data version
lastSyncedAt: Date | null; // Last sync timestamp
}useDataStore
Advanced data store with schema validation and query capabilities.
useDataStore<T>(config: StoreConfig)
interface StoreConfig<T> {
name: string;
schema: {
[K in keyof T]: {
type: 'string' | 'number' | 'boolean' | 'object' | 'array';
required?: boolean;
};
};
}
const [store, { isLoading, error }] = useDataStore<Item>({
name: "items",
schema: {
id: { type: "string", required: true },
name: { type: "string", required: true },
count: { type: "number" },
},
});
// CRUD operations
await store.create(item);
await store.read(id);
await store.update(id, partial);
await store.delete(id);
await store.getAll();
// Query with operators
const results = await store.query({
name: { contains: "search" },
count: { gte: 10 },
});
// Query operators
{ eq: value } // equals
{ ne: value } // not equals
{ gt: value } // greater than
{ gte: value } // greater than or equal
{ lt: value } // less than
{ lte: value } // less than or equal
{ in: [values] } // in array
{ contains: str } // string containsuseOnlineStatus
Track the user's online/offline status reactively.
useOnlineStatus(): boolean
const isOnline = useOnlineStatus();
return (
<div>
{!isOnline && (
<Banner variant="warning">
You're offline. Changes will sync when you reconnect.
</Banner>
)}
</div>
);usePWAInstall
Handle PWA installation prompts for standalone Constructs.
usePWAInstall(): { canInstall: boolean, install: () => Promise<boolean>, dismiss: () => void }
const { canInstall, install, dismiss } = usePWAInstall();
return (
<>
{canInstall && (
<div className="fixed bottom-4 right-4 p-4 bg-card rounded-lg shadow-lg">
<p>Install this app for offline access?</p>
<div className="flex gap-2 mt-2">
<Button onClick={install}>Install</Button>
<Button variant="ghost" onClick={dismiss}>Not now</Button>
</div>
</div>
)}
</>
);useIsInstalledPWA
Detect if the Construct is running as an installed PWA.
useIsInstalledPWA(): boolean
const isInstalled = useIsInstalledPWA();
// Show different UI for installed vs browser
return (
<header>
{!isInstalled && <Button onClick={install}>Install App</Button>}
{isInstalled && <span>Running as standalone app</span>}
</header>
);useImportExport
Export and import user data in JSON or CSV format.
useImportExport(): { exportData: (opts) => Promise<Blob>, importData: (opts) => Promise<void> }
const { exportData, importData } = useImportExport();
// Export data
const handleExport = async () => {
const blob = await exportData({
format: "json", // "json" | "csv"
stores: ["todos"], // which data stores to export
compress: true, // gzip compression (optional)
});
// Create download link
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "my-data.json";
a.click();
};
// Import data
const handleImport = async (file: File) => {
await importData({
file,
onConflict: "replace", // "skip" | "replace" | "error"
});
};useInterAppComm
Enable communication between Constructs using BroadcastChannel.
useInterAppComm(config): InterAppComm
const comm = useInterAppComm({
permissions: ["read:todos", "write:todos"], // requested permissions
});
// Send a message to other Constructs
comm.send({
type: "TODO_CREATED",
payload: { id: "1", text: "New todo" },
});
// Subscribe to messages
useEffect(() => {
const unsubscribe = comm.subscribe("TODO_UPDATED", (payload) => {
console.log("Todo updated:", payload);
});
return unsubscribe;
}, []);
// Request/response pattern
const response = await comm.request({
type: "GET_TODOS",
timeout: 5000,
});useRuntime
Manage Construct version and handle auto-updates.
useRuntime(): RuntimeInfo
const {
version, // Current version
latestVersion, // Latest available version
updateAvailable, // boolean
update, // () => Promise<void>
autoUpdate, // Enable auto-updates
} = useRuntime();
return (
<>
{updateAvailable && (
<Banner>
Version {latestVersion} available.
<Button onClick={update}>Update now</Button>
</Banner>
)}
</>
);Best Practices
Use useConstructData for simple state
It handles persistence, loading states, and sync automatically.
Use useDataStore for complex data
When you need queries, schema validation, or multiple collections.
Handle loading states
Always check isLoading before rendering data-dependent UI.
Show sync status to users
Let users know when their data is saved, syncing, or offline.