useOffline()

Report whether the app has lost connectivity so Client Components can render offline UI.

Signature
import { useOffline } from 'next/offline'

const isOffline = useOffline()
  • Added in Next.js v16.x.0

Return value

booleantrue when a network request has failed or the browser fired an offline event; false when online, when rendering on the server, and as the initial value before hydration.

Errors

Does not throw. Without experimental.useOffline enabled in next.config it silently returns false forever.

Changelog

  • PHP v16.x.0useOffline hook introduced as an experimental feature, gated behind the experimental.useOffline config flag. The API is subject to change and is not recommended for production.

Experimental. The API may change and is not recommended for production.

useOffline returns a boolean saying whether the app is currently offline. It is only the readable surface of a larger feature: turning on experimental.useOffline in next.config enables connectivity detection plus automatic retry of blocked navigation, prefetch, and Server Action requests, and exposes this hook so Client Components can reflect that state.

Reach for it when you are building a PWA or an app used on flaky networks and the user needs to understand why nothing is happening — a persistent banner, or an offline-aware loading.tsx that says "waiting for connection" instead of spinning forever. When the connection returns, Next.js retries the blocked request and the dynamic content streams in without any code from you.

What trips people up

  • Without the config flag the hook always returns false. No warning, no error — the banner simply never appears.
  • It returns false during server rendering and on the first client render, so the offline UI can only show after hydration. Do not branch layout on it in a way that causes a hydration mismatch.
  • It is not navigator.onLine. The value also flips to true when a request fails, which catches captive portals and dead uplinks that navigator.onLine reports as online.

Example

Example · typescript
'use client'

import { useOffline } from 'next/offline'

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

  if (!isOffline) {
    return null
  }

  return (
    <div role="status" className="offline-banner">
      You are offline. Some content may be unavailable.
    </div>
  )
}

When to use it

  • Show a persistent banner in the root layout when the user loses connectivity.
  • Explain a stalled navigation in loading.tsx with "waiting for connection" instead of a spinner.
  • Disable a submit button that would only queue a doomed Server Action.
  • Swap a live data widget for a cached-value notice while offline.

Frequently asked questions

Why does useOffline always return false?

The experimental.useOffline flag is not enabled in next.config. Without it the hook is inert and returns false; it also returns false during server rendering and on the first client render before hydration.

Is useOffline different from navigator.onLine?

Yes. It reports true both when the browser fires an offline event and when a network request actually fails, so it catches captive portals and dead connections that navigator.onLine still reports as online.

Is useOffline safe to use in production?

No. It is experimental, gated behind a config flag, and the Next.js docs state the API may change and is not recommended for production.

More examples

Enabling the feature and mounting the banner

The flag is what makes the hook return anything but false; it also switches on automatic retry of blocked navigation, prefetch, and Server Action requests. Mounting the banner in the root layout puts it on every route without repeating it per page.

Example · typescript
// next.config.ts
import type { NextConfig } from 'next'

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

export default nextConfig

// app/layout.tsx
import { OfflineBanner } from './components/offline-banner'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <OfflineBanner />
        {children}
      </body>
    </html>
  )
}

An offline-aware Suspense fallback

Navigating while offline renders the prefetched static shell immediately, but the dynamic content behind the Suspense boundary blocks on the network. This fallback explains the wait rather than implying progress; when connectivity returns, the retried request resolves and the real content streams in.

Example · typescript
// app/destination/loading.tsx
'use client'

import { useOffline } from 'next/offline'

export default function Loading() {
  const isOffline = useOffline()

  return (
    <div>
      {isOffline ? 'Waiting for connection to load this page...' : 'Loading...'}
    </div>
  )
}

Guarding an action that needs the network

Because the hook returns false on the server and on the first client render, the button ships enabled and only disables after hydration detects the offline state. That ordering keeps the server HTML and the first client render identical, avoiding a hydration mismatch.

Example · typescript
'use client'

import { useOffline } from 'next/offline'

export function PublishButton({ action }: { action: () => Promise<void> }) {
  const isOffline = useOffline()

  return (
    <>
      <button type="button" onClick={() => action()} disabled={isOffline}>
        Publish
      </button>
      {isOffline ? <p role="status">Reconnect to publish.</p> : null}
    </>
  )
}

Discussion

  • Be the first to comment on this lesson.