Use Promise.all() for parallel requests or sequential async/await for dependent calls. Parallel execution: const [users, posts, comments] = await Promise.all([api.getUsers(), api.getPosts(), api.getComments()]); executes simultaneously, waits for all to complete, total time = slowest request (not sum). Sequential: const user = await api.getUser(id); const posts = await api.getPosts(user.id); total time = sum of all requests, use only when dependent. Error handling parallel: use Promise.allSettled() to continue if some fail: const results = await Promise.allSettled([req1, req2, req3]); results.forEach(r => { if (r.status === 'fulfilled') use(r.value); else handle(r.reason); });. Rate limiting concurrent: use p-limit package: import pLimit from 'p-limit'; const limit = pLimit(3); const promises = ids.map(id => limit(() => api.get(id))); await Promise.all(promises); limits to 3 concurrent requests. Cancellation: use AbortController: const controller = new AbortController(); fetch(url, { signal: controller.signal }); controller.abort(); cancels request. Best practices: (1) Batch requests when API supports (graphql, /api/batch), (2) Use Promise.all for independent requests, (3) Limit concurrency to avoid server overload (max 5-10), (4) Handle partial failures gracefully, (5) Add request timeouts, (6) Cache responses when appropriate. Vue: composable pattern: const { data, execute } = useConcurrentFetch([url1, url2, url3]);.
Web Performance Optimization FAQ & Answers
21 expert Web Performance Optimization answers researched from official documentation. Every answer cites authoritative sources you can verify.
unknown
21 questionsLazy loading defers loading non-critical resources until needed, improving initial load time. Benefits: (1) Faster initial page load: load only visible content first, reduces time to interactive by 50-70%, improves Core Web Vitals (LCP <2.5s), (2) Reduced bandwidth: users who don't scroll don't download below-fold content, saves 200% data in some cases, (3) Better mobile experience: critical on slow connections, preserves cellular data. Lazy load strategies: (1) Images: use Intersection Observer API: const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; observer.unobserve(img); } }); }); Native:
(browser handles automatically, 95%+ support in 2025). (2) Routes: Vue Router lazy load: { path: '/about', component: () => import('./About.vue') } loads on navigation, not app start. (3) Components: defineAsyncComponent: const HeavyChart = defineAsyncComponent(() => import('./HeavyChart.vue')); (4) Third-party scripts: load YouTube embed only when user clicks play thumbnail. Monitoring: use Intersection Observer to track when elements load, send analytics. Best practices: (1) Lazy load images below fold, (2) Eager load hero image, (3) Preload critical route chunks, (4) Show loading skeleton while fetching, (5) Consider content priority (ads last, content first).
Use WebSockets for bidirectional real-time communication. WebSocket setup: const ws = new WebSocket('wss://api.example.com/updates'); ws.onopen = () => console.log('Connected'); ws.onmessage = (event) => { const data = JSON.parse(event.data); updateUI(data); }; ws.onerror = (error) => handleError(error); ws.onclose = () => reconnect();. Vue composable: const { data, status, send, close } = useWebSocket('wss://api.example.com'); watch(data, (newData) => { items.value.push(newData); });. Reconnection logic: auto-reconnect with exponential backoff: let attempts = 0; const reconnect = () => { setTimeout(() => { attempts++; connect(); }, Math.min(1000 * 2 ** attempts, 30000)); }. Alternatives: (1) Server-Sent Events (SSE): one-way server → client, simpler than WebSocket, automatic reconnection: const eventSource = new EventSource('/api/stream'); eventSource.onmessage = (event) => handleUpdate(event.data);. (2) Long polling: fallback for old browsers, less efficient: const poll = async () => { const data = await fetch('/api/updates'); handleUpdate(data); poll(); };. (3) Firebase/Supabase: managed real-time databases, subscribe to data changes. Best practices: (1) Heartbeat/ping to keep connection alive, (2) Graceful degradation (fallback to polling), (3) Handle connection state (connecting, connected, disconnected), (4) Throttle rapid updates (debounce UI updates), (5) Close connection on component unmount, (6) Use binary protocol (MessagePack) for large data. Use cases: chat apps, live notifications, collaborative editing, stock prices, sports scores.
CDNs distribute content globally to reduce latency and improve performance. How CDNs work: (1) Content cached on edge servers near users (330+ cities for Cloudflare), (2) User requests served from nearest edge (not origin server), (3) Reduces latency by 50-70% (Cloudflare: <50ms for 95% of users). Benefits: (1) Performance: 50% faster load times, improves Core Web Vitals (LCP), edge servers closer than origin (e.g., US user → EU origin: 200ms+ vs CDN: 20-50ms). (2) Scalability: handles traffic spikes (viral content, Black Friday), 405 Tbps capacity (Cloudflare), no origin server overload. (3) Cost savings: reduces origin bandwidth (CDN serves cached content), fewer origin server requests = lower hosting costs. (4) Security: DDoS protection built-in (absorbs attack traffic), free SSL certificates, bot protection, WAF (Web Application Firewall). (5) Reliability: redundancy across locations, failover if origin down, 99.99% uptime SLA. CDN usage: static assets (JS, CSS, images, videos), dynamic content (with short TTL), API responses (with cache headers). Implementation: Cloudflare (free tier, easy setup), configure cache headers: Cache-Control: public, max-age=31536000 (1 year for static assets), Cache-Control: public, max-age=60 (1 min for dynamic). Purge cache on deploy: cloudflare-cli purge or API. Best practices: (1) Use CDN for all static assets, (2) Set long cache times + versioned URLs, (3) Optimize images before CDN, (4) Enable compression (gzip/brotli), (5) Use HTTP/2 or HTTP/3. Statistics 2025: 70%+ internet traffic via CDNs.
Use multi-layer caching for optimal performance. Caching layers: (1) Browser cache: HTTP headers control browser caching, Cache-Control: max-age=31536000, immutable for hashed assets (app.abc123.js), Cache-Control: no-cache for HTML (always revalidate). (2) Service Worker cache: cache API responses, images, app shell, offline-first PWA: self.addEventListener('fetch', (event) => { event.respondWith(caches.match(event.request).then(response => response || fetch(event.request))); });. (3) Memory cache: in-app caching (composable): const cache = new Map(); export function useCachedFetch(url) { if (cache.has(url)) return cache.get(url); const data = fetch(url); cache.set(url, data); return data; }. (4) CDN cache: edge servers cache static content globally, set via HTTP headers. (5) Server cache: Redis, Memcached for API responses, database query results. Strategies: (1) Cache-First: try cache, fallback to network (static assets), (2) Network-First: try network, fallback to cache (API data), (3) Stale-While-Revalidate: serve cached, update in background (balance speed and freshness). Cache invalidation: (1) Time-based: expire after TTL (max-age=3600), (2) Event-based: purge on content update, (3) Versioned URLs: change URL on update (app.v2.js), old version cached, new version fetched. Best practices: (1) Cache immutable assets forever (hashed filenames), (2) No-cache HTML (references versioned assets), (3) Use ETags for validation, (4) Compress responses (gzip/brotli), (5) Monitor cache hit ratio (aim >80%). Performance impact: cache hit = <50ms response, cache miss = 200-500ms.
Code splitting breaks bundles into smaller chunks for faster initial load. Benefits: (1) Reduces initial bundle size by 50-70%, loads only code needed for current route, improves Time to Interactive (TTI <3.8s), (2) Parallel loading: browser downloads multiple small chunks simultaneously vs one large bundle, (3) Better caching: update one chunk doesn't invalidate entire bundle. Vite code splitting: automatic with dynamic imports: const UserProfile = () => import('./UserProfile.vue'); Vite creates separate chunk for UserProfile, loaded when component used. Manual chunk configuration: vite.config.ts: build: { rollupOptions: { output: { manualChunks: { vendor: ['vue', 'vue-router', 'pinia'], utils: ['lodash', 'axios'] } } } }. Route-based splitting: routes.map(route => ({ path: route.path, component: () => import(./views/${route.component}.vue) })); each route separate chunk. Component-level: defineAsyncComponent(() => import('./HeavyChart.vue')); loads only when rendered. Webpack vs Vite: Vite automatically code splits with zero config, uses Rollup for optimized output, faster build times. Best practices: (1) Split by routes (most effective), (2) Separate vendor code (changes less frequently), (3) Lazy load below-fold components, (4) Keep critical path small (<30kb), (5) Prefetch likely-next routes: . Monitoring: use Webpack Bundle Analyzer or rollup-plugin-visualizer to identify large chunks. Result: initial load 100kb vs 500kb (5× faster on 3G).
Use Service Workers to enable offline-first Progressive Web Apps (PWAs). Service Worker basics: register: if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js'); }. Service Worker file (sw.js): const CACHE_NAME = 'v1'; const CACHE_ASSETS = ['/index.html', '/app.js', '/styles.css', '/logo.png']; self.addEventListener('install', (event) => { event.waitUntil(caches.open(CACHE_NAME).then(cache => cache.addAll(CACHE_ASSETS))); }); self.addEventListener('fetch', (event) => { event.respondWith(caches.match(event.request).then(response => response || fetch(event.request))); });. Strategies: (1) Cache-First: serve from cache, fallback to network (static assets, images), (2) Network-First: try network, fallback to cache (API data, news), (3) Cache-then-Network: serve cached immediately, update with network response (best UX). Workbox library (2025 standard): simplifies Service Worker: import { precacheAndRoute, registerRoute, CacheFirst, NetworkFirst } from 'workbox-precaching'; precacheAndRoute(self.__WB_MANIFEST); registerRoute(({request}) => request.destination === 'image', new CacheFirst());. Offline UI: detect: window.addEventListener('online', () => showOnlineUI()); window.addEventListener('offline', () => showOfflineUI());. Background Sync: queue failed requests, retry when online: self.addEventListener('sync', (event) => { if (event.tag === 'sync-form') { event.waitUntil(sendFormData()); } });. Best practices: (1) Cache app shell (HTML, CSS, JS), (2) Show offline indicator, (3) Queue writes for background sync, (4) Test with DevTools offline mode, (5) Update Service Worker carefully (users may have old version). PWA benefits: installable, offline-capable, fast, native-like experience.
Service Workers are programmable network proxies enabling offline experiences and background features. Capabilities: (1) Cache management: intercept network requests, serve from cache, cache responses, implement offline-first strategy. (2) Background sync: queue operations when offline, sync when connection restored, ensure data consistency. (3) Push notifications: receive push messages, show notifications even when app closed, re-engage users. (4) Performance: cache static assets (instant load), prefetch resources, reduce server load. Lifecycle: Install (download and cache assets), Activate (clean old caches), Fetch (intercept network requests). Registration: if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/sw.js').then(reg => console.log('SW registered')).catch(err => console.error('SW failed', err)); }); }. Scope: controls pages under registration path, /sw.js controls all pages, /app/sw.js controls only /app/* pages. Update strategy: browser checks for new SW on navigation, new SW waits until all tabs closed (or skipWaiting()), activate new version. Debugging: Chrome DevTools → Application tab → Service Workers, see active workers, update, unregister. Security: HTTPS required (except localhost), prevents man-in-the-middle attacks, origin-scoped. Use cases: PWAs, offline web apps, background data sync, push notifications, performance optimization. Browser support: 95%+ in 2025 (Chrome, Firefox, Safari, Edge). Best practices: (1) Cache intelligently (not everything), (2) Version caches (v1, v2), (3) Clean old caches on activate, (4) Test update process, (5) Provide fallback for unsupported browsers.
Use Push API with Service Workers for web push notifications. Setup process: (1) Request permission: const permission = await Notification.requestPermission(); if (permission === 'granted') subscribe();. (2) Get push subscription: const registration = await navigator.serviceWorker.ready; const subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: PUBLIC_VAPID_KEY });. Send subscription to server for storage. (3) Backend sends push: use web-push library (Node.js): import webpush from 'web-push'; webpush.setVapidDetails('mailto:[email protected]', PUBLIC_KEY, PRIVATE_KEY); webpush.sendNotification(subscription, JSON.stringify({ title: 'New Message', body: 'You have 1 new message' }));. (4) Service Worker receives push: self.addEventListener('push', (event) => { const data = event.data.json(); event.waitUntil(self.registration.showNotification(data.title, { body: data.body, icon: '/icon.png', badge: '/badge.png', data: { url: '/messages' } })); });. (5) Handle notification click: self.addEventListener('notificationclick', (event) => { event.notification.close(); event.waitUntil(clients.openWindow(event.notification.data.url)); });. VAPID keys: generate once: webpush.generateVAPIDKeys(), use for push authentication. Best practices: (1) Request permission at appropriate time (after user action), (2) Allow users to opt-out (settings page), (3) Send relevant notifications only, (4) Include action buttons, (5) Deep link to specific content. Browser support: Chrome, Firefox, Edge (95%+ in 2025), Safari added support recently. Use cases: new messages, updates, reminders, breaking news, order status.
WebSockets provide full-duplex communication channel for real-time bidirectional data flow. How WebSockets work: (1) HTTP handshake upgrades to WebSocket protocol, (2) Persistent TCP connection established, (3) Both client and server can send messages anytime, (4) Low overhead (<2 bytes per message frame). Advantages over HTTP polling: (1) Lower latency: no request/response cycle, messages sent immediately (<50ms vs 500ms+ polling), (2) Reduced bandwidth: no HTTP headers on every message, 1 connection vs repeated connections, (3) Server push: server initiates messages without client request. Implementation: const ws = new WebSocket('wss://api.example.com/chat'); ws.onopen = () => { ws.send(JSON.stringify({ type: 'join', room: 'lobby' })); }; ws.onmessage = (event) => { const message = JSON.parse(event.data); displayMessage(message); }; ws.onerror = (error) => reconnect(); ws.onclose = () => reconnect();. Backend (Node.js with ws): import { WebSocketServer } from 'ws'; const wss = new WebSocketServer({ port: 8080 }); wss.on('connection', (ws) => { ws.on('message', (data) => { wss.clients.forEach(client => client.send(data)); }); });. Scaling: use Redis pub/sub for multi-server: each server subscribes to Redis channel, messages broadcast to all connected clients across servers. Use cases: chat applications, collaborative editing (Google Docs), live gaming, real-time dashboards, live sports scores, stock trading platforms. When to use: frequent updates (>1/second), low latency critical (<100ms), bidirectional communication needed. Alternatives: Server-Sent Events (SSE) for one-way server→client, Long polling for old browser support. Best practices: (1) Implement reconnection with backoff, (2) Heartbeat/ping to detect disconnection, (3) Handle connection state, (4) Use message queuing for reliability, (5) Compress large messages.
Use conflict resolution strategies and operational transforms for multi-client sync. Patterns: (1) Last-write-wins (LWW): simplest, timestamp-based, last update overwrites, loses concurrent edits. Example: { data: 'value', timestamp: 1699999999 }. (2) Operational Transform (OT): transforms concurrent operations to converge to same state, used by Google Docs, complex to implement. (3) Conflict-Free Replicated Data Types (CRDTs): mathematically guarantee convergence, no central authority needed, Yjs library for JavaScript. (4) Version vectors: track causal ordering, detect conflicts, manual resolution. Real-time sync approaches: WebSocket server broadcasts changes: const server = new WebSocketServer(); server.on('connection', (ws) => { ws.on('message', (data) => { server.clients.forEach(client => { if (client !== ws) client.send(data); }); }); });. Firebase Realtime Database: automatic sync across clients: const ref = firebase.database().ref('documents/doc1'); ref.on('value', snapshot => updateLocal(snapshot.val())); ref.set({ content: 'new value' }); all clients receive update. Supabase Realtime: PostgreSQL change streams: const channel = supabase.channel('db-changes').on('postgres_changes', { event: '*', schema: 'public', table: 'todos' }, payload => handleChange(payload)).subscribe();. Conflict resolution: (1) Automatic: use CRDTs (Yjs, Automerge), (2) Manual: detect conflicts, show diff UI, user chooses resolution. Optimistic updates: apply change locally immediately, sync with server, rollback if conflict. Best practices: (1) Use established libraries (Yjs for collaborative editing), (2) Implement offline queue, (3) Show sync status (synced, syncing, conflict), (4) Test with multiple clients simultaneously, (5) Handle partial failures gracefully. Use cases: collaborative docs, real-time whiteboards, multiplayer games, shared task lists.
PWAs combine web and native app features for enhanced user experience. Key characteristics: (1) Installable: Add to Home Screen, appears in app drawer, launches in standalone window (no browser UI), icon on home screen. (2) Offline-capable: Service Workers cache assets and data, works without network connection, background sync when online. (3) Fast: cached resources load instantly, optimized performance (Core Web Vitals), smooth animations (60fps). (4) Engaging: push notifications, badging API, periodic background sync. (5) Safe: HTTPS required, secure by default. Requirements: (1) Web app manifest (manifest.json): { "name": "My PWA", "short_name": "PWA", "icons": [{"src": "/icon-192.png", "sizes": "192x192"}], "start_url": "/", "display": "standalone", "theme_color": "#3B82F6" }. (2) Service Worker for offline support. (3) HTTPS. (4) Responsive design. Benefits over native apps: (1) No app store approval (instant updates), (2) Smaller size (<5MB vs 50MB+ native), (3) Single codebase (web + mobile), (4) Discoverable via search engines, (5) Cross-platform (iOS, Android, desktop). Installation prompt: automatically shown by browser when criteria met, or trigger manually: let deferredPrompt; window.addEventListener('beforeinstallprompt', (e) => { deferredPrompt = e; showInstallButton(); }); installButton.click(() => deferredPrompt.prompt());. Tools: Vite Plugin PWA automates Service Worker and manifest generation. Browser support: 95%+ (Chrome, Edge, Safari, Firefox) in 2025. Best practices: (1) Optimize for offline-first, (2) Cache app shell, (3) Lazy load non-critical resources, (4) Test with Lighthouse PWA audit (score >90), (5) Handle updates gracefully. Examples: Twitter Lite, Starbucks, Pinterest (all PWAs).
Use Promise.all() for parallel independent requests or sequential async/await for dependent calls. Parallel: const [users, posts, comments] = await Promise.all([api.getUsers(), api.getPosts(), api.getComments()]); executes simultaneously, total time = slowest request. Sequential: const user = await api.getUser(id); const posts = await api.getPosts(user.id); use when dependent. Error handling: Promise.allSettled() continues if some fail. Rate limiting: use p-limit package: import pLimit from 'p-limit'; const limit = pLimit(3); limits to 3 concurrent. Cancellation: AbortController: const controller = new AbortController(); fetch(url, { signal: controller.signal }); controller.abort();. Best practices: batch requests when API supports, use Promise.all for independent, limit concurrency (max 5-10), handle partial failures, add timeouts, cache responses.
Use WebSockets for bidirectional real-time communication. Setup: const ws = new WebSocket('wss://api.example.com/updates'); ws.onopen = () => console.log('Connected'); ws.onmessage = (event) => { const data = JSON.parse(event.data); updateUI(data); };. Reconnection: auto-reconnect with exponential backoff: let attempts = 0; const reconnect = () => setTimeout(() => connect(), Math.min(1000 * 2 ** attempts++, 30000));. Alternatives: (1) Server-Sent Events (SSE) for one-way server→client with automatic reconnection. (2) Long polling for old browser fallback. (3) Firebase/Supabase for managed real-time databases. Best practices: heartbeat/ping to keep alive, graceful degradation (fallback to polling), handle connection state, throttle rapid updates, close on unmount, use binary protocol (MessagePack) for large data. Use cases: chat, live notifications, collaborative editing, stock prices.
CDNs distribute content globally to reduce latency and improve performance. How it works: (1) Content cached on edge servers near users (330+ cities for Cloudflare). (2) User requests served from nearest edge. (3) Reduces latency by 50-70% (Cloudflare: <50ms for 95% of users). Benefits: 50% faster load times, improves Core Web Vitals, handles traffic spikes (405 Tbps capacity), reduces origin bandwidth, free SSL, DDoS protection, 99.99% uptime. CDN usage: static assets (JS, CSS, images, videos), dynamic content (short TTL), API responses. Implementation: configure cache headers: Cache-Control: public, max-age=31536000 (1 year for static), Cache-Control: public, max-age=60 (1 min dynamic). Best practices: use for all static assets, long cache + versioned URLs, optimize images before CDN, enable compression (gzip/brotli), use HTTP/2 or HTTP/3. 70%+ internet traffic via CDNs (2025).
Use multi-layer caching for optimal performance. Layers: (1) Browser cache via HTTP headers: Cache-Control: max-age=31536000, immutable for hashed assets, Cache-Control: no-cache for HTML. (2) Service Worker cache for API responses, images, app shell (offline-first PWA). (3) Memory cache in-app: const cache = new Map(); cache.set(url, data);. (4) CDN cache for static content globally. (5) Server cache: Redis, Memcached for API responses, DB query results. Strategies: Cache-First (static assets), Network-First (API data), Stale-While-Revalidate (serve cached, update in background). Cache invalidation: time-based (max-age=3600), event-based (purge on update), versioned URLs (app.v2.js). Best practices: cache immutable assets forever (hashed filenames), no-cache HTML, use ETags, compress responses, monitor cache hit ratio (aim >80%). Cache hit = <50ms response, miss = 200-500ms.
Use Service Workers to enable offline-first Progressive Web Apps (PWAs). Service Worker basics: register with navigator.serviceWorker.register('/sw.js');. Cache assets: self.addEventListener('install', (event) => { event.waitUntil(caches.open('v1').then(cache => cache.addAll(['/index.html', '/app.js', '/styles.css']))); });. Intercept requests: self.addEventListener('fetch', (event) => { event.respondWith(caches.match(event.request).then(response => response || fetch(event.request))); });. Strategies: Cache-First (static assets), Network-First (API data), Cache-then-Network (best UX). Workbox library simplifies: import { precacheAndRoute } from 'workbox-precaching'; precacheAndRoute(self.__WB_MANIFEST);. Offline UI: detect with window.addEventListener('offline', () => showOfflineUI());. Background Sync queues failed requests, retries when online. Best practices: cache app shell, show offline indicator, queue writes for background sync, test with DevTools offline mode, update Service Worker carefully.
Use Push API with Service Workers for web push notifications. Setup: (1) Request permission: const permission = await Notification.requestPermission(); if (permission === 'granted') subscribe();. (2) Get push subscription: const subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: PUBLIC_VAPID_KEY });. Send subscription to server. (3) Backend sends push using web-push library: webpush.sendNotification(subscription, JSON.stringify({ title: 'New Message', body: 'You have 1 new message' }));. (4) Service Worker receives: self.addEventListener('push', (event) => { const data = event.data.json(); event.waitUntil(self.registration.showNotification(data.title, { body: data.body, icon: '/icon.png' })); });. (5) Handle click: self.addEventListener('notificationclick', (event) => { event.waitUntil(clients.openWindow(event.notification.data.url)); });. Best practices: request permission appropriately, allow opt-out, send relevant notifications only, include action buttons, deep link to content. Browser support: Chrome, Firefox, Edge, Safari (95%+ in 2025).
WebSockets provide full-duplex communication channel for real-time bidirectional data flow. How it works: (1) HTTP handshake upgrades to WebSocket protocol. (2) Persistent TCP connection established. (3) Both client and server can send messages anytime. (4) Low overhead (<2 bytes per frame). Advantages over HTTP polling: Lower latency (no request/response cycle, <50ms vs 500ms+ polling), reduced bandwidth (no HTTP headers on every message, 1 connection vs repeated), server push (server initiates without client request). Implementation: const ws = new WebSocket('wss://api.example.com/chat'); ws.onmessage = (event) => displayMessage(JSON.parse(event.data));. Scaling: use Redis pub/sub for multi-server. Use cases: chat, collaborative editing, live gaming, real-time dashboards, stock trading. When to use: frequent updates (>1/second), low latency critical (<100ms), bidirectional needed. Best practices: implement reconnection with backoff, heartbeat/ping, handle connection state, use message queuing, compress large messages.
Use conflict resolution strategies and operational transforms for multi-client sync. Patterns: (1) Last-write-wins (LWW) - simplest, timestamp-based, loses concurrent edits. (2) Operational Transform (OT) - transforms concurrent operations to converge, used by Google Docs, complex to implement. (3) Conflict-Free Replicated Data Types (CRDTs) - mathematically guarantee convergence, Yjs library for JavaScript. (4) Version vectors - track causal ordering, detect conflicts, manual resolution. Real-time sync: WebSocket broadcasts changes to all clients. Firebase Realtime Database: automatic sync: firebase.database().ref('documents/doc1').on('value', snapshot => updateLocal(snapshot.val()));. Supabase Realtime: PostgreSQL change streams. Conflict resolution: automatic (use CRDTs) or manual (show diff UI, user chooses). Optimistic updates: apply locally immediately, sync with server, rollback if conflict. Best practices: use established libraries (Yjs), implement offline queue, show sync status, test with multiple clients, handle partial failures. Use cases: collaborative docs, real-time whiteboards, multiplayer games, shared task lists.
PWAs combine web and native app features for enhanced user experience. Key characteristics: (1) Installable - Add to Home Screen, appears in app drawer, launches in standalone window. (2) Offline-capable - Service Workers cache assets/data, works without network, background sync when online. (3) Fast - cached resources load instantly, optimized performance (Core Web Vitals), smooth 60fps animations. (4) Engaging - push notifications, badging API, periodic background sync. (5) Safe - HTTPS required. Requirements: Web app manifest (manifest.json with name, icons, start_url, display, theme_color), Service Worker for offline, HTTPS, responsive design. Benefits over native: no app store approval (instant updates), smaller size (<5MB vs 50MB+ native), single codebase (web + mobile), discoverable via search, cross-platform (iOS, Android, desktop). Installation prompt: automatically shown by browser or trigger manually. Tools: Vite Plugin PWA. Browser support: 95%+ in 2025. Best practices: optimize offline-first, cache app shell, lazy load non-critical, test with Lighthouse (score >90). Examples: Twitter Lite, Starbucks, Pinterest.