Capítulo 63 de 456

Offline support

Core Idea

Experimental feature that keeps navigations, RSC fetches, prefetches, and Server Actions pending (instead of throwing) when the network drops, automatically retrying once connectivity returns, plus a hook (useOffline) to surface connectivity state in the UI.

Key Concepts

  • experimental.useOffline: config flag that prevents failed navigation/RSC fetch/prefetch/Server Action from throwing on network failure; keeps the request pending and retries once online.
  • useOffline() hook (from next/offline): returns true when the browser fires an offline event or a Next.js request fails; flips to false after a successful background connectivity check. More reliable than navigator.onLine, which only reflects the OS interface, not actual internet reachability.
  • Cache Components + Partial Prefetching synergy: Cache Components lets the <Suspense> boundary sit close to uncached data with an App Shell around it; Partial Prefetching makes that shell the unit <Link> prefetches, so it's ready to render offline.
  • Scope limitation: only applies to soft navigations into prefetched routes and Server Action calls from the current page — a full page reload while offline still fails (would need a service worker / PWA approach for that).
  • Without Cache Components: a route-level loading.tsx gives the same offline behavior at the segment level, as the boundary Next.js prefetches as the route's shell.
  • Client-side fetch/data libraries excluded: fetch() calls made directly in a Client Component, or via React Query/SWR, follow their own retry policy — not covered by useOffline's automatic retry.

Code Examples

// next.config.ts — enabling offline support alongside Cache Components
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  cacheComponents: true,
  partialPrefetching: true,
  experimental: {
    useOffline: true,
  },
}

export default nextConfig
  • O que demonstra: configuração completa recomendada para habilitar offline support.
// app/dashboard/connectivity-fallback.tsx — Suspense fallback aware of connectivity
'use client'

import { useOffline } from 'next/offline'

export function ConnectivityFallback() {
  const isOffline = useOffline()

  return (
    <p>
      {isOffline
        ? 'Waiting for connection to load this section...'
        : 'Loading...'}
    </p>
  )
}
  • O que demonstra: como usar useOffline para diferenciar "carregando" de "esperando reconexão" no próprio fallback do Suspense.
// app/ping/ping-form.tsx — Server Action retry with offline-aware label
'use client'

import { useState, useTransition } from 'react'
import { useOffline } from 'next/offline'
import { ping } from './actions'

export function PingForm() {
  const [pongs, setPongs] = useState<string[]>([])
  const [pending, startTransition] = useTransition()
  const isOffline = useOffline()

  function handleSubmit() {
    startTransition(async () => {
      const pong = await ping()
      setPongs((prev) => [pong, ...prev])
    })
  }

  const label = pending
    ? isOffline
      ? 'Pinging (offline, will retry)...'
      : 'Pinging...'
    : 'Ping'

  return (
    <form action={handleSubmit}>
      <button type="submit" disabled={pending}>
        {label}
      </button>
    </form>
  )
}
  • O que demonstra: Server Action que não precisa de try/catch para lidar com falha de rede, o useOffline + useTransition cobrem o feedback visual.

Reference Tables

Not applicable — no tabular API reference in this source range.

Anti-patterns

  • Confiar em navigator.onLine para detectar offline real: só reflete a interface de rede do SO, reporta true mesmo sem acesso real à internet (ex: WiFi sem upstream).
  • Testar em modo dev: o guia recomenda next build && next start para testar; dev mode não é referência confiável para comportamento offline.
  • Esperar que um reload completo funcione offline: full page reload sempre depende da rede para entregar o HTML; só um service worker (PWA) resolveria isso.
  • Não colocar <Link> no viewport da página de origem: sem isso o App Shell da rota destino não é prefetched e não fica disponível offline.

Key Takeaways

  1. Feature experimental (não recomendada para produção); habilitar via experimental.useOffline.
  2. useOffline retorna false durante SSR e hidratação inicial; o primeiro valor confiável só vem depois do mount no browser.
  3. Sem Cache Components, loading.tsx no nível da rota reproduz o mesmo comportamento offline.
  4. Um banner global (useOffline no root layout) complementa o fallback local do Suspense para comunicar o estado de conectividade em toda a aplicação.
  5. Clicar num link durante uma Server Action pendente offline pode parecer travado, ambos aguardam o mesmo sinal de conectividade e resolvem juntos quando a rede volta.

Connects To

  • Migrating to Cache Components (ch060): pré-requisito recomendado para posicionar o <Suspense> o mais perto possível do dado não-cacheado.
  • Progressive Web Apps guide: caminho alternativo via service worker para cobrir full-page-reload offline, fora do escopo desta feature.