xiaohongshu
xiaohongshu
Network resilience patterns for frontend - retry logic, exponential backoff, error handling, offline detection
Full skill instructions
Comprehensive patterns for building robust network operations in frontend applications.
When to Use:
When NOT to Use:
Implementation Pattern:
/**
* Retry an operation with exponential backoff
*
* @param operation - Async function to retry
* @param maxAttempts - Maximum number of attempts (default: 4)
* @param initialDelay - Initial delay in ms (default: 2000)
* @param maxDelay - Maximum delay in ms (default: 30000)
* @returns Result of operation or throws after all attempts exhausted
*/
async function retryWithBackoff<T>(
operation: () => Promise<T>,
maxAttempts = 4,
initialDelay = 2000,
maxDelay = 30000
): Promise<T> {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await operation();
} catch (error) {
// Last attempt - rethrow error
if (attempt === maxAttempts) {
throw error;
}
// Calculate delay with exponential backoff
const delay = Math.min(
initialDelay * Math.pow(2, attempt - 1),
maxDelay
);
console.log(`Attempt ${attempt}/${maxAttempts} failed, retrying in ${delay}ms...`);
// Wait before next attempt
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error('All retry attempts exhausted');
}
Usage Example:
// Loading external SDK with retry
const sdkLoaded = await retryWithBackoff(
async () => {
if (!sdk.loaded) {
throw new Error('SDK not ready');
}
return true;
},
4, // 4 attempts
2000, // Start with 2s delay
30000 // Max 30s delay
);
Backoff Schedule Examples:
4 attempts, 2s initial:
Attempt 1 → wait 2s → Attempt 2 → wait 4s → Attempt 3 → wait 8s → Attempt 4
Total: ~14s
4 attempts, 3s initial:
Attempt 1 → wait 3s → Attempt 2 → wait 6s → Attempt 3 → wait 12s → Attempt 4
Total: ~21s
5 attempts, 2s initial:
Attempt 1 → wait 2s → Attempt 2 → wait 4s → Attempt 3 → wait 8s →
Attempt 4 → wait 16s → Attempt 5
Total: ~30s
Error Taxonomy:
type NetworkErrorType =
| 'offline' // No internet connection
| 'timeout' // Request/load took too long
| 'blocked' // Blocked by browser/extension/firewall
| 'not_found' // Resource doesn't exist (404)
| 'server_error' // Server returned 5xx
| 'unauthorized' // Auth failed (401/403)
| 'rate_limited' // Too many requests (429)
| 'network_error' // Generic network failure
| 'unknown'; // Unclassified error
interface NetworkError {
type: NetworkErrorType;
message: string;
userMessage: string; // User-friendly message
canRetry: boolean; // Should we offer retry?
autoRetry: boolean; // Should we auto-retry?
}
Error Detection:
function classifyNetworkError(error: any): NetworkError {
// Offline detection
if (!navigator.onLine) {
return {
type: 'offline',
message: 'Network offline',
userMessage: 'No internet connection. Please check your network.',
canRetry: true,
autoRetry: false // Don't auto-retry if offline
};
}
// Timeout
if (error.name === 'TimeoutError' || error.message?.includes('timeout')) {
return {
type: 'timeout',
message: error.message,
userMessage: 'Connection is very slow. Please check your internet.',
canRetry: true,
autoRetry: true // Auto-retry timeouts
};
}
// Blocked (CSP, ad blocker, CORS)
if (error.message?.includes('blocked') ||
error.message?.includes('CSP') ||
error.name === 'SecurityError') {
return {
type: 'blocked',
message: error.message,
userMessage: 'Resource blocked. Please disable ad blockers.',
canRetry: true,
autoRetry: false // User must fix blocker first
};
}
// HTTP status codes
if (error.status === 404) {
return {
type: 'not_found',
message: error.message,
userMessage: 'Resource not found.',
canRetry: false,
autoRetry: false
};
}
if (error.status >= 500) {
return {
type: 'server_error',
message: error.message,
userMessage: 'Server error. Please try again later.',
canRetry: true,
autoRetry: true // Server errors are often transient
};
}
if (error.status === 401 || error.status === 403) {
return {
type: 'unauthorized',
message: error.message,
userMessage: 'Please sign in again.',
canRetry: false,
autoRetry: false
};
}
if (error.status === 429) {
return {
type: 'rate_limited',
message: error.message,
userMessage: 'Too many requests. Please wait a moment.',
canRetry: true,
autoRetry: true // With longer backoff
};
}
// Generic network error
return {
type: 'network_error',
message: error.message || 'Unknown error',
userMessage: 'Network error. Please try again.',
canRetry: true,
autoRetry: true
};
}
Implementation:
import { onMount } from 'svelte';
let isOnline = $state(true);
onMount(() => {
// Initial check
isOnline = navigator.onLine;
// Listen for online/offline events
const handleOnline = () => { isOnline = true; };
const handleOffline = () => { isOnline = false; };
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
});
Usage in Components:
{#if !isOnline}
<div class="offline-banner">
⚠️ No internet connection. Some features may not work.
</div>
{/if}
<button
onclick={handleAction}
disabled={!isOnline}
>
Submit
</button>
For External SDK Loading:
<script lang="ts">
let loadingState = $state<'idle' | 'loading' | 'ready' | 'error'>('idle');
let loadingTime = $state(0);
let attemptNumber = $state(0);
let error = $state<NetworkError | null>(null);
// Show progress message after 3s
let showProgress = $derived(loadingTime > 3);
async function loadSDK() {
loadingState = 'loading';
loadingTime = 0;
// Update loading time every second
const interval = setInterval(() => {
loadingTime++;
}, 1000);
try {
await retryWithBackoff(
async () => {
attemptNumber++;
// SDK load logic
if (!sdk.loaded) throw new Error('Not ready');
return true;
},
4,
2000
);
clearInterval(interval);
loadingState = 'ready';
} catch (err) {
clearInterval(interval);
loadingState = 'error';
error = classifyNetworkError(err);
}
}
</script>
{#if loadingState === 'loading'}
<div class="loading-container">
<Spinner />
{#if attemptNumber > 1}
<p>Connecting... (attempt {attemptNumber}/4)</p>
{:else if showProgress}
<p>This is taking longer than expected ({loadingTime}s)...</p>
{:else}
<p>Loading...</p>
{/if}
</div>
{:else if loadingState === 'error'}
<ErrorMessage
error={error}
onRetry={error.canRetry ? loadSDK : undefined}
/>
{/if}
Guidelines:
Never call network operations during SSR
// ❌ BAD - Will fail during SSR
const data = await fetch('/api/data');
// ✅ GOOD - Only runs client-side
onMount(async () => {
const data = await fetch('/api/data');
});
Provide initial loading state in SSR HTML
<!-- This renders during SSR -->
<div class="content">
{#if data}
<DataDisplay {data} />
{:else}
<!-- This spinner is in SSR HTML -->
<Spinner />
<p>Loading content...</p>
{/if}
</div>
Handle hydration gracefully
let data = $state(null);
let isLoading = $state(true); // true during SSR and hydration
onMount(async () => {
// This runs after hydration
data = await loadData();
isLoading = false;
});
When planning any feature with network operations, ensure:
All network operations must include tests for:
Playwright Example:
test('SDK loads with retry on slow network', async ({ page, context }) => {
// Simulate slow 3G
await context.route('**/clerk.sdk.js', async (route) => {
await new Promise(resolve => setTimeout(resolve, 4000));
await route.continue();
});
await page.goto('/login');
// Should show loading state
await expect(page.locator('.spinner')).toBeVisible();
// Should show progress message after 3s
await expect(page.locator('text=Taking longer')).toBeVisible({ timeout: 4000 });
// Should eventually load
await expect(page.locator('.clerk-sign-in')).toBeVisible({ timeout: 10000 });
});
// BAD - Gives up after one timeout
const loaded = await waitForSDK(10000);
if (!loaded) {
showError('Failed to load');
}
// GOOD - Retries with backoff
const loaded = await retryWithBackoff(
() => waitForSDK(5000),
4,
2000
);
// BAD - Same message for all errors
catch (error) {
showError('Something went wrong. Please try again.');
}
// GOOD - Differentiated messages
catch (error) {
const networkError = classifyNetworkError(error);
showError(networkError.userMessage);
if (networkError.canRetry) {
showRetryButton();
}
}
// BAD - Attempts network call while offline
async function loadData() {
return await fetch('/api/data');
}
// GOOD - Checks offline state first
async function loadData() {
if (!navigator.onLine) {
throw new Error('offline');
}
return await fetch('/api/data');
}
// BAD - Will fail during SSR
const data = await fetch('/api/data');
// GOOD - Only client-side
let data = $state(null);
onMount(async () => {
data = await fetch('/api/data');
});
Last Updated: 2025-11-06 Applies To: All frontend network operations Mandatory: YES - Plan agents must reference this during planning
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience