Offline-first mobile apps with local storage, sync queues, conflict resolution. Use for offline functionality, data sync, connectivity handling, or encountering sync conflicts, queue management, storage limits, network transition errors.
Builds offline-first mobile apps with local storage and sync queues. Use when implementing offline functionality, handling network connectivity changes, or resolving data synchronization conflicts.
/plugin marketplace add secondsky/claude-skills/plugin install mobile-offline-support@claude-skillsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
references/native-implementations.mdBuild offline-first mobile applications with local storage and synchronization.
import AsyncStorage from '@react-native-async-storage/async-storage';
import NetInfo from '@react-native-community/netinfo';
class OfflineManager {
constructor() {
this.syncQueue = [];
this.isOnline = true;
// Maximum items in sync queue before discarding oldest
this.MAX_SYNC_QUEUE_LENGTH = 1000;
NetInfo.addEventListener(state => {
this.isOnline = state.isConnected;
if (this.isOnline) this.processQueue();
});
}
/**
* Fetch data from server.
* TODO: Replace with actual API endpoint implementation.
*/
async fetchFromServer(key) {
try {
// Example implementation - replace with your API
const response = await fetch(`${API_BASE_URL}/data/${key}`);
if (!response.ok) {
throw new Error(`Server returned ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('fetchFromServer failed:', error);
throw new Error(`Failed to fetch ${key}: ${error.message}`);
}
}
/**
* Sync data to server.
* TODO: Replace with actual API endpoint implementation.
*/
async syncToServer(key, data) {
try {
// Example implementation - replace with your API
const response = await fetch(`${API_BASE_URL}/data/${key}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (!response.ok) {
throw new Error(`Server returned ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('syncToServer failed:', error);
throw new Error(`Failed to sync ${key}: ${error.message}`);
}
}
async getData(key) {
const cached = await AsyncStorage.getItem(key);
if (cached) return JSON.parse(cached);
if (this.isOnline) {
const data = await this.fetchFromServer(key);
await AsyncStorage.setItem(key, JSON.stringify(data));
return data;
}
return null;
}
async saveData(key, data) {
await AsyncStorage.setItem(key, JSON.stringify(data));
if (this.isOnline) {
await this.syncToServer(key, data);
} else {
// Add to queue
this.syncQueue.push({ key, data, timestamp: Date.now() });
// Enforce queue bounds - discard oldest if exceeded
while (this.syncQueue.length > this.MAX_SYNC_QUEUE_LENGTH) {
const discarded = this.syncQueue.shift();
console.warn(`Sync queue full - discarded oldest item: ${discarded.key}`);
}
// Persist trimmed queue
await AsyncStorage.setItem('syncQueue', JSON.stringify(this.syncQueue));
}
}
async processQueue() {
const failedItems = [];
for (const item of this.syncQueue) {
try {
await this.syncToServer(item.key, item.data);
} catch (err) {
console.error('Sync failed:', err);
failedItems.push(item);
}
}
this.syncQueue = failedItems;
if (failedItems.length === 0) {
await AsyncStorage.removeItem('syncQueue');
} else {
await AsyncStorage.setItem('syncQueue', JSON.stringify(failedItems));
}
}
}
function resolveConflict(local, server) {
// Last-write-wins
if (local.updatedAt > server.updatedAt) return local;
return server;
// Or merge changes
// return { ...server, ...local };
}
function OfflineIndicator() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
return NetInfo.addEventListener(state => {
setIsOnline(state.isConnected);
});
}, []);
if (isOnline) return null;
return (
<View style={styles.banner}>
<Text>You're offline. Changes will sync when connected.</Text>
</View>
);
}
See references/native-implementations.md for:
Use when working with Payload CMS projects (payload.config.ts, collections, fields, hooks, access control, Payload API). Use when debugging validation errors, security issues, relationship queries, transactions, or hook behavior.