-
Notifications
You must be signed in to change notification settings - Fork 0
Offline Mode Quick Start
github-actions[bot] edited this page Sep 2, 2026
·
46 revisions
Get offline mode working in your nself-chat app in 5 minutes.
Add the offline indicator to your main layout:
// src/app/layout.tsx or src/components/layout/main-layout.tsx
import { OfflineIndicator } from '@/components/ui/offline-indicator'
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<div>
<OfflineIndicator position="top" detailed={true} />
{children}
</div>
)
}Initialize the sync manager when the app starts:
// src/app/layout.tsx or src/contexts/app-provider.tsx
'use client'
import { useEffect } from 'react'
import { getSyncManager } from '@/lib/offline/sync-manager'
export function AppProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
// Initialize sync manager
const syncManager = getSyncManager({
autoSync: true,
syncInterval: 30000, // 30 seconds
syncOnReconnect: true,
})
syncManager.initialize().catch(console.error)
return () => {
syncManager.shutdown()
}
}, [])
return <>{children}</>
}Save messages to cache when received:
// src/hooks/use-messages.ts
import { messageStorage } from '@/lib/offline/offline-storage'
import { useEffect } from 'react'
export function useMessages(channelId: string) {
const { data: messages } = useQuery(GET_MESSAGES, {
variables: { channelId },
})
// Cache messages for offline access
useEffect(() => {
if (messages) {
const cached = messages.map((msg) => ({
id: msg.id,
channelId: msg.channel_id,
content: msg.content,
senderId: msg.user_id,
senderName: msg.user?.displayName || 'Unknown',
createdAt: new Date(msg.created_at),
reactions: msg.reactions || [],
attachments: msg.attachments || [],
}))
messageStorage.saveMany(cached).catch(console.error)
}
}, [messages])
return { messages }
}Queue messages sent while offline:
// src/components/chat/message-input.tsx
import { queueStorage } from '@/lib/offline/offline-storage'
import { useOfflineStatus } from '@/hooks/use-offline'
import { v4 as uuid } from 'uuid'
export function MessageInput({ channelId }: { channelId: string }) {
const isOnline = useOfflineStatus()
const [sendMessage] = useMutation(SEND_MESSAGE)
const handleSend = async (content: string) => {
if (isOnline) {
// Send normally
await sendMessage({ variables: { channelId, content } })
} else {
// Queue for later
await queueStorage.add({
id: uuid(),
type: 'send_message',
payload: {
channelId,
content,
tempId: uuid(),
},
priority: 'high',
status: 'pending',
createdAt: new Date(),
updatedAt: new Date(),
retryCount: 0,
maxRetries: 5,
lastError: null,
})
}
}
return (
<input
type="text"
onKeyDown={(e) => {
if (e.key === 'Enter') {
handleSend(e.currentTarget.value)
e.currentTarget.value = ''
}
}}
placeholder={isOnline ? 'Type a message...' : 'Offline - message will send later'}
/>
)
}You now have:
- β Offline indicator showing connection status
- β Automatic message caching
- β Offline message queueing
- β Auto-sync on reconnect
- β Background sync (on mobile)
- Open DevTools β Network tab
- Select "Offline" from the throttling dropdown
- Send a message
- See it appear with a pending indicator
- Go back "Online"
- Watch it sync automatically!
import { useSync } from '@/hooks/use-sync'
function SyncButton() {
const { isSyncing, syncNow } = useSync()
return (
<button onClick={syncNow} disabled={isSyncing}>
{isSyncing ? 'Syncing...' : 'Sync Now'}
</button>
)
}import { usePendingCount } from '@/hooks/use-offline'
function PendingBadge() {
const count = usePendingCount()
if (count === 0) return null
return <span className="badge">{count} pending</span>
}import { SyncProgressToast } from '@/components/ui/sync-progress'
function App() {
return (
<>
<YourApp />
<SyncProgressToast />
</>
)
}import { getAttachmentCache } from '@/lib/offline/attachment-cache'
async function downloadAttachment(url: string, messageId: string) {
const cache = getAttachmentCache()
await cache.initialize()
const attachment = await cache.download(
url,
{
id: uuid(),
messageId,
channelId: 'channel-1',
name: 'file.jpg',
type: 'image/jpeg',
size: 1024000,
},
(progress) => {
console.log(`Downloaded ${progress.percent}%`)
}
)
// Get data URL for display
const dataUrl = await cache.getDataUrl(attachment.id)
return dataUrl
}// src/app/layout.tsx (Capacitor app)
import { backgroundFetchService } from '@/lib/ios/background-fetch'
useEffect(() => {
if (Capacitor.getPlatform() === 'ios') {
backgroundFetchService.start()
backgroundFetchService.onFetch('app', (result) => {
if (result.newData) {
// Show notification or update UI
}
})
}
}, [])// src/app/layout.tsx (Capacitor app)
import { workManager } from '@/lib/android/work-manager'
useEffect(() => {
if (Capacitor.getPlatform() === 'android') {
workManager.initialize({
syncIntervalMinutes: 15,
requiresCharging: false,
requiresWifi: false,
})
}
}, [])Customize offline behavior:
import { getSyncManager } from '@/lib/offline/sync-manager'
import { getAttachmentCache } from '@/lib/offline/attachment-cache'
// Configure sync
const syncManager = getSyncManager({
autoSync: true,
syncInterval: 60000, // 1 minute
batteryThreshold: 15, // Pause sync below 15%
batchSize: 50,
})
// Configure attachment cache
const cache = getAttachmentCache({
maxSize: 200 * 1024 * 1024, // 200MB
maxFileSize: 50 * 1024 * 1024, // 50MB per file
generateThumbnails: true,
})Check the queue:
import { queueStorage } from '@/lib/offline/offline-storage'
const pending = await queueStorage.getPending()
console.log('Pending items:', pending)Check sync state:
import { getSyncManager } from '@/lib/offline/sync-manager'
const syncManager = getSyncManager()
const state = syncManager.getState()
console.log('Sync state:', state)Check storage stats:
import { getStorageStats } from '@/lib/offline/offline-storage'
const stats = await getStorageStats()
console.log('Storage:', stats)- Read the full documentation
- Implement conflict resolution
- Add progress indicators
- Customize sync behavior
- Test on mobile devices
For issues or questions:
- Check the troubleshooting section in the full docs
- Review the test files for examples
- Look at the integration tests
Happy offline coding! π
nself-chat v0.3.0 | GitHub | Issues | Discussions | Demo
Edit this page | MIT License | Β© 2026
(See π Security section below for 2FA, PIN Lock, and security audits.)
(Search lives in π Reference below.)
- π¬ Advanced Messaging
- π E2EE Setup
- π Search Setup
- π Call Management
- πΊ Live Streaming
- π₯οΈ Screen Sharing
- πΉ Video Calling
- ποΈ Voice Calling
- π± Mobile Optimization
- π§ͺ Testing
- π i18n
- π API Overview
- π Complete Reference
- π» API Examples
- π€ Bot API
- π Auth API
- π GraphQL Schema
- π Deployment Overview
- π³ Docker
- βΈοΈ Kubernetes
- β Helm Charts
- β Production Checklist
- π Production Validation
- π’ Multi-Tenant
- ποΈ Architecture
- π Diagrams
- ποΈ Database Schema
- π Project Structure
- π TypeScript Types
- π SPORT Reference
- π 2FA
- π¬ Messaging
- π Call Management
- π Call State Machine
- π E2EE
- πΊ Live Streaming
- π± Mobile Calls
- π PIN Lock
- π Polls
- π₯οΈ Screen Sharing
- π Search
- π Social Media
- ποΈ Voice Calling
- π Security Overview
- π‘οΈ Security Audit
- β‘ Performance
- π Best Practices
- π 2FA
- π PIN Lock
- π E2EE
- π‘οΈ E2EE Audit
v1.0.0 β’ 2026