Draft Status Hook
Lightweight React hook to track draft status without DOM interaction.
Overview
useDraftStatus provides:
- Real-time draft status tracking
- No DOM manipulation (read-only)
- Expiration detection
- Manual refresh capability
Why useDraftStatus?
Unlike useFormAutosave, useDraftStatus:
- ✅ Doesn't attach to DOM elements
- ✅ Doesn't save or restore drafts
- ✅ Only reads from storage
- ✅ Perfect for status indicators, banners, and UI components
Basic Usage
import { useDraftStatus } from '@form-guardian/react';
function DraftBanner() {
const { hasDraft, updatedAt, isExpired, isChecking } = useDraftStatus('my-form');
if (isChecking) {
return <div>Checking for draft...</div>;
}
if (!hasDraft) {
return null;
}
if (isExpired) {
return <div>Your draft has expired</div>;
}
return (
<div>
Draft saved at {new Date(updatedAt!).toLocaleString()}
</div>
);
}
API Reference
Parameters
useDraftStatus(formId, options?)
formId(string, required) - Unique identifier for the formoptions(object, optional) - Configuration options
Options
interface UseDraftStatusOptions {
includeOrigin?: boolean; // Include origin in draft ID (default: true)
storagePrefix?: string; // Storage key prefix (default: 'fg')
ttl?: number | { // Time to live for drafts
days?: number;
hours?: number;
minutes?: number;
};
}
Return Value
interface UseDraftStatusResult {
hasDraft: boolean; // Whether draft exists
isExpired: boolean; // Whether draft has expired
updatedAt: number | null; // Timestamp of draft (if exists)
isChecking: boolean; // Whether status is being checked
refresh: () => Promise<void>; // Manually refresh status
clear: () => Promise<void>; // Clear draft from storage
}
Examples
Simple Status Indicator
import { useDraftStatus } from '@form-guardian/react';
function DraftIndicator() {
const { hasDraft, updatedAt } = useDraftStatus('contact-form');
if (!hasDraft) return null;
return (
<div className="draft-indicator">
💾 Draft saved {new Date(updatedAt!).toLocaleString()}
</div>
);
}
Status with Expiration
import { useDraftStatus } from '@form-guardian/react';
function DraftStatus() {
const { hasDraft, isExpired, updatedAt } = useDraftStatus('my-form', {
ttl: { days: 7 },
});
if (!hasDraft) {
return <div>No draft available</div>;
}
if (isExpired) {
return <div className="expired">⚠️ Draft has expired</div>;
}
return (
<div className="active">
✓ Draft saved {new Date(updatedAt!).toLocaleString()}
</div>
);
}
Manual Refresh
import { useDraftStatus } from '@form-guardian/react';
function DraftStatusWithRefresh() {
const { hasDraft, updatedAt, isChecking, refresh } = useDraftStatus('my-form');
return (
<div>
{isChecking ? (
<span>Checking...</span>
) : hasDraft ? (
<span>Draft: {new Date(updatedAt!).toLocaleString()}</span>
) : (
<span>No draft</span>
)}
<button onClick={() => refresh()}>Refresh</button>
</div>
);
}
Clear Draft
import { useDraftStatus } from '@form-guardian/react';
function DraftControls() {
const { hasDraft, updatedAt, clear } = useDraftStatus('my-form');
const handleClear = async () => {
await clear();
// Draft is now cleared
// hasDraft will be false on next render
};
return (
<div>
{hasDraft && (
<div>
<span>Draft: {new Date(updatedAt!).toLocaleString()}</span>
<button onClick={handleClear}>Clear Draft</button>
</div>
)}
</div>
);
}
Use Cases
1. Draft Banner
Show a banner when a draft exists:
function DraftBanner() {
const { hasDraft, updatedAt } = useDraftStatus('checkout-form');
if (!hasDraft) return null;
return (
<div className="banner">
You have an unsaved draft from {new Date(updatedAt!).toLocaleString()}
</div>
);
}
2. Navigation Indicator
Show draft status in navigation:
function Navigation() {
const { hasDraft } = useDraftStatus('contact-form');
return (
<nav>
<Link href="/contact">Contact {hasDraft && '💾'}</Link>
</nav>
);
}
3. Form Header Status
Show status in form header:
function FormHeader() {
const { hasDraft, updatedAt, isExpired } = useDraftStatus('my-form', {
ttl: { days: 7 },
});
return (
<header>
<h1>My Form</h1>
{hasDraft && (
<div className="status">
{isExpired ? (
<span className="expired">Draft expired</span>
) : (
<span>Last saved: {new Date(updatedAt!).toLocaleString()}</span>
)}
</div>
)}
</header>
);
}
4. Multiple Forms
Track status for multiple forms:
function MultiFormStatus() {
const contactStatus = useDraftStatus('contact-form');
const checkoutStatus = useDraftStatus('checkout-form');
return (
<div>
<div>Contact: {contactStatus.hasDraft ? '💾' : '✗'}</div>
<div>Checkout: {checkoutStatus.hasDraft ? '💾' : '✗'}</div>
</div>
);
}
Best Practices
1. Match Configuration
When using with useFormAutosave, match the configuration:
// useFormAutosave
const { formRef } = useFormAutosave('my-form', {
includeOrigin: true,
storagePrefix: 'fg',
ttl: { days: 7 },
});
// useDraftStatus (must match)
const { hasDraft } = useDraftStatus('my-form', {
includeOrigin: true, // Must match
storagePrefix: 'fg', // Must match
ttl: { days: 7 }, // Must match
});
2. Handle Loading State
const { hasDraft, isChecking } = useDraftStatus('my-form');
if (isChecking) {
return <Skeleton />; // Show loading state
}
// Render actual content
Performance
- Lightweight - No DOM manipulation, only storage reads
- Efficient - Only reads when needed
- Cached - Status is cached until refresh or broadcast update
- SSR Safe - Returns safe defaults on server
Next Steps
- 📖 API Reference - Complete API documentation
- ⚙️ Batching - Optimize save performance
- 📊 Analytics Events - Track form interactions