🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

next

Package Overview
Dependencies
Maintainers
4
Versions
3871
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

next - npm Package Compare versions

Comparing version
16.3.0-canary.101
to
16.3.0-canary.102
+436
dist/docs/01-app/02-guides/offline-support.md
---
title: Handling connectivity drops
description: How a Next.js app can recover when the network drops mid-fetch or mid-Server Action, and how to communicate that state to the user.
nav_title: Offline support
version: experimental
related:
title: Learn more
description: The hook and config flag used in this guide.
links:
- app/api-reference/functions/use-offline
- app/api-reference/config/next-config-js/useOffline
- app/guides/progressive-web-apps
---
Network failures during a soft navigation, a data fetch, or a mutation throw errors in the client. Without explicit handling, the UI either breaks or you have to build a fallback UI that asks the user to retry.
With [`experimental.useOffline`](/docs/app/api-reference/config/next-config-js/useOffline) enabled, a failed navigation, RSC data fetch, prefetch, or Server Action no longer throws when the network is down. Next.js keeps it pending and retries it once the connection returns.
While the request is pending, the UI sits in its loading state (a Suspense fallback, or a pending transition for a Server Action), which looks the same as a slow server. Use the [`useOffline`](/docs/app/api-reference/functions/use-offline) hook to give users feedback when the app is offline.
Requests you issue directly with `fetch()` inside a Client Component, or through a client-side data library like React Query or SWR, stay under that library's own retry policy. See [How retry works](/docs/app/api-reference/config/next-config-js/useOffline#how-retry-works) for the framework's detection and polling behavior.
## Example
We will build a live-metrics page that fetches fresh data on every request, plus a ping form that calls a Server Action.
- Source: [github.com/vercel-labs/use-offline](https://github.com/vercel-labs/use-offline)
- Live demo: [use-offline.labs.vercel.dev](https://use-offline.labs.vercel.dev)
The companion demo has two versions of this dashboard: `/without-feedback` uses a generic loading fallback, `/with-feedback` uses a connectivity-aware one. The next sections of this guide walk through building each.
## Enable offline detection and see the default behavior
Turn on `experimental.useOffline`. This guide also enables [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) and [Partial Prefetching](/docs/app/api-reference/config/next-config-js/partialPrefetching). Cache Components lets you place the Suspense boundary as close as possible to the uncached data, with the [App Shell](/docs/app/glossary#app-shell) rendered around it. Partial Prefetching makes that App Shell the unit a `<Link>` prefetches, so it is ready to render when a navigation happens offline.
Without Cache Components, a route-level [`loading.tsx`](/docs/app/api-reference/file-conventions/loading) gives you the same offline behavior at the segment level. See [Without Cache Components](#without-cache-components) below.
```ts filename="next.config.ts" switcher
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
partialPrefetching: true,
experimental: {
useOffline: true,
},
}
export default nextConfig
```
```js filename="next.config.js" switcher
module.exports = {
cacheComponents: true,
partialPrefetching: true,
experimental: {
useOffline: true,
},
}
```
You also need a source page with a `<Link>` to the dashboard. Next.js prefetches the App Shell of any `<Link>` that enters the viewport, and that prefetch is what makes the shell available offline.
```tsx filename="app/page.tsx" switcher
import Link from 'next/link'
export default function Home() {
return (
<nav>
<Link href="/dashboard">Dashboard</Link>
</nav>
)
}
```
```jsx filename="app/page.js" switcher
import Link from 'next/link'
export default function Home() {
return (
<nav>
<Link href="/dashboard">Dashboard</Link>
</nav>
)
}
```
Then build the dashboard itself: a static shell and an uncached data section inside a `<Suspense>` boundary. `getLiveMetrics` is an uncached function that makes a fetch request to an endpoint; every call hits the network.
```tsx filename="app/dashboard/page.tsx" switcher
import { Suspense } from 'react'
import { getLiveMetrics } from '../lib/data'
export default function Dashboard() {
return (
<section>
<h1>Live metrics</h1>
<Suspense fallback={<p>Loading...</p>}>
<MetricsTable />
</Suspense>
</section>
)
}
async function MetricsTable() {
const { services } = await getLiveMetrics()
// render services
}
```
```jsx filename="app/dashboard/page.js" switcher
import { Suspense } from 'react'
import { getLiveMetrics } from '../lib/data'
export default function Dashboard() {
return (
<section>
<h1>Live metrics</h1>
<Suspense fallback={<p>Loading...</p>}>
<MetricsTable />
</Suspense>
</section>
)
}
async function MetricsTable() {
const { services } = await getLiveMetrics()
// render services
}
```
Load the home page. With the `<Link>` in the viewport, the dashboard's static shell is prefetched. Go offline (see [Testing](#testing) below) and click through to `/dashboard`.
The title and container render from the static shell. The `Loading...` fallback stays on screen indefinitely because the uncached `getLiveMetrics()` call cannot complete. The user sees the same spinner they would see for a slow server.
Toggle back to **Online**. The metrics table streams in automatically. Next.js retried the request on its own, no client code involved.
> [!NOTE]
> This feature only applies to soft navigations into prefetched routes and Server Action calls from the current page. A full page reload while offline still fails because the browser needs the network to deliver the HTML; full offline loads would need a service worker (see the [Progressive Web Apps](/docs/app/guides/progressive-web-apps) guide).
Next, replace the generic fallback UI with one that reads the connectivity state.
## Report connectivity inside the Suspense fallback
`useOffline` returns `true` when the browser fires an `offline` event **or** when a navigation, prefetch, or Server Action fetch fails. It flips back to `false` when a background connectivity check succeeds. This is more reliable than `navigator.onLine`, which only reflects the OS network interface and still reports `true` for a device on WiFi with no upstream internet.
Create a client component that picks its message based on the hook.
```tsx filename="app/dashboard/connectivity-fallback.tsx" switcher
'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>
)
}
```
```jsx filename="app/dashboard/connectivity-fallback.js" switcher
'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>
)
}
```
> [!NOTE]
> `useOffline` returns `false` during server-side rendering and initial hydration. The first accurate value is whatever the browser reports after the app mounts.
Pass it as the Suspense fallback.
```tsx filename="app/dashboard/page.tsx" highlight={3,9} switcher
import { Suspense } from 'react'
import { getLiveMetrics } from '../lib/data'
import { ConnectivityFallback } from './connectivity-fallback'
export default function Dashboard() {
return (
<section>
<h1>Live metrics</h1>
<Suspense fallback={<ConnectivityFallback />}>
<MetricsTable />
</Suspense>
</section>
)
}
```
```jsx filename="app/dashboard/page.js" highlight={3,9} switcher
import { Suspense } from 'react'
import { getLiveMetrics } from '../lib/data'
import { ConnectivityFallback } from './connectivity-fallback'
export default function Dashboard() {
return (
<section>
<h1>Live metrics</h1>
<Suspense fallback={<ConnectivityFallback />}>
<MetricsTable />
</Suspense>
</section>
)
}
```
Navigating to the dashboard while offline, the fallback now reads "Waiting for connection to load this section..." Restore connectivity and the metrics stream in as the fallback disappears.
The fallback only shows on this page, and only while its Suspense boundary is waiting. In this app, we add a banner in the root layout so connectivity state is visible everywhere.
```tsx filename="app/offline-banner.tsx" switcher
'use client'
import { useOffline } from 'next/offline'
export function OfflineBanner() {
const isOffline = useOffline()
if (!isOffline) {
return null
}
return (
<div role="status">
Offline. Pending requests will retry once you are back online.
</div>
)
}
```
```jsx filename="app/offline-banner.js" switcher
'use client'
import { useOffline } from 'next/offline'
export function OfflineBanner() {
const isOffline = useOffline()
if (!isOffline) {
return null
}
return (
<div role="status">
Offline. Pending requests will retry once you are back online.
</div>
)
}
```
Add it to the root layout.
```tsx filename="app/layout.tsx" highlight={1,7} switcher
import { OfflineBanner } from './offline-banner'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<body>
<OfflineBanner />
{children}
</body>
</html>
)
}
```
```jsx filename="app/layout.js" highlight={1,7} switcher
import { OfflineBanner } from './offline-banner'
export default function RootLayout({ children }) {
return (
<html>
<body>
<OfflineBanner />
{children}
</body>
</html>
)
}
```
The banner shows on every route while offline, and hides when connectivity returns.
For most apps, the pending loading state is enough. Add a banner in the root layout to communicate the connectivity state across the app. To surface that state right where the content is loading, make the route's Suspense fallback itself offline-aware with `useOffline`.
The same pattern extends to parameterized routes. A route like `/chats/[id]` renders its shared [App Shell](/docs/app/glossary#app-shell) when you navigate to `/chats/42` offline, and the dynamic messages behind its `<Suspense>` boundary load when the connection returns.
If the route also prefetches its per-link URL data ahead of the click, `/chats/42` renders its messages from that prefetch immediately, even offline, instead of waiting for the connection to return. See [runtime prefetching](/docs/app/guides/runtime-prefetching) to learn more.
## Retry Server Actions after the network returns
Without the flag, a Server Action called with no network throws a fetch error and the awaited promise rejects. Your form has to catch the rejection and decide what to do: show an error, retry, or queue it somewhere.
With `experimental.useOffline` enabled, that failure never reaches your code. The call stays pending until the connection returns, the request runs again, and the awaited promise resolves with the server's response. No try/catch, no retry loop, no reconnection handler in the component.
The button is still going to sit there looking frozen, though. Combine `useTransition` with `useOffline` to give it an offline-aware label.
```ts filename="app/ping/actions.ts" switcher
'use server'
export async function ping(): Promise<string> {
return new Date().toISOString()
}
```
```js filename="app/ping/actions.js" switcher
'use server'
export async function ping() {
return new Date().toISOString()
}
```
```tsx filename="app/ping/ping-form.tsx" switcher
'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>
<ul>
{pongs.map((t) => (
<li key={t}>{t}</li>
))}
</ul>
</form>
)
}
```
```jsx filename="app/ping/ping-form.js" switcher
'use client'
import { useState, useTransition } from 'react'
import { useOffline } from 'next/offline'
import { ping } from './actions'
export function PingForm() {
const [pongs, setPongs] = useState([])
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>
<ul>
{pongs.map((t) => (
<li key={t}>{t}</li>
))}
</ul>
</form>
)
}
```
Clicking **Ping** while offline disables the button and changes its label to "Pinging (offline, will retry)...". Restoring connectivity resolves the awaited `ping()` call, appends the timestamp to the list, and reverts the label to "Ping". No second click, no client-side retry code.
> [!NOTE]
> While offline, clicking a link during a pending Server Action may appear to do nothing. The link's navigation also needs the network and queues behind the same connectivity signal as the action. Both resolve when the connection returns.
## Testing
Test this feature with `next build && next start`. Dev mode is not a reliable reference for offline behavior.
In Chrome, use [DevTools > Network > **Offline**](https://developer.chrome.com/docs/devtools/network/reference#offline); in Firefox, use the [Network Monitor's throttling menu](https://firefox-source-docs.mozilla.org/devtools-user/network_monitor/throttling/index.html). For a real-world test, toggle airplane mode on your laptop or phone, disconnect WiFi, or unplug the network cable.
## Without Cache Components
A route-level [`loading.tsx`](/docs/app/api-reference/file-conventions/loading) does the same job. It gives Next.js a boundary to prefetch as the route's shell, so the shell renders offline and the page resumes once the network returns. For how `loading.tsx` prefetching works, see [Prefetching](/docs/app/guides/prefetching). The `useOffline` hook, banner, and Server Action retry all behave the same way.
## Next steps
- [`useOffline` hook reference](/docs/app/api-reference/functions/use-offline)
- [`experimental.useOffline` config reference](/docs/app/api-reference/config/next-config-js/useOffline)
- [`loading.tsx` file convention](/docs/app/api-reference/file-conventions/loading)
- [Progressive Web Apps guide](/docs/app/guides/progressive-web-apps) for service-worker-based offline caching
---
title: useOffline
description: API Reference for the useOffline hook.
version: experimental
related:
links:
- app/api-reference/config/next-config-js/useOffline
- app/guides/progressive-web-apps
---
The `useOffline` hook returns a boolean indicating whether the app is currently offline. Use it to render connectivity-aware UI, such as a banner when the user loses their network connection, or an offline-aware Suspense fallback.
The hook is one piece of a larger feature. Enabling the [`experimental.useOffline`](/docs/app/api-reference/config/next-config-js/useOffline) config option turns on offline connectivity detection and automatic retry of blocked navigation, prefetch, and Server Action requests, and exposes this hook so Client Components can read the state.
Without the flag, this hook always returns `false`.
```js filename="next.config.js"
module.exports = {
experimental: {
useOffline: true,
},
}
```
```tsx filename="app/offline-status.tsx" switcher
'use client'
import { useOffline } from 'next/offline'
export function OfflineStatus() {
const isOffline = useOffline()
return <div>{isOffline ? 'Offline' : 'Online'}</div>
}
```
```jsx filename="app/offline-status.js" switcher
'use client'
import { useOffline } from 'next/offline'
export function OfflineStatus() {
const isOffline = useOffline()
return <div>{isOffline ? 'Offline' : 'Online'}</div>
}
```
For details on how connectivity is detected and requests retried, see [How retry works](/docs/app/api-reference/config/next-config-js/useOffline#how-retry-works).
## Parameters
```tsx
const isOffline = useOffline()
```
`useOffline` does not take any parameters.
## Returns
`useOffline` returns a `boolean`:
| Value | Meaning |
| ------- | --------------------------------------------------------------------------------------------------------- |
| `true` | The app is offline. A network request has failed, or the browser has fired an `offline` event. |
| `false` | The app is online, or rendering on the server. This is also the initial value before hydration completes. |
## Examples
### Show an offline banner
Render a persistent banner whenever the user loses connectivity.
```tsx filename="app/components/offline-banner.tsx" switcher
'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>
)
}
```
```jsx filename="app/components/offline-banner.js" switcher
'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>
)
}
```
Render it in the root layout so the banner shows on every route:
```tsx filename="app/layout.tsx" switcher
import { OfflineBanner } from './components/offline-banner'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<body>
<OfflineBanner />
{children}
</body>
</html>
)
}
```
```jsx filename="app/layout.js" switcher
import { OfflineBanner } from './components/offline-banner'
export default function RootLayout({ children }) {
return (
<html>
<body>
<OfflineBanner />
{children}
</body>
</html>
)
}
```
### Offline-aware Suspense fallback
When a user navigates to a route while offline, the prefetched static shell renders immediately but the dynamic content behind a `<Suspense>` boundary blocks on the network. For example, use `useOffline` inside a [`loading.tsx`](/docs/app/api-reference/file-conventions/loading) file to explain why the content is taking longer than expected.
```tsx filename="app/destination/loading.tsx" switcher
'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>
)
}
```
```jsx filename="app/destination/loading.js" switcher
'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>
)
}
```
When connectivity is restored, Next.js retries the blocked request and the dynamic content streams in automatically.
## Version History
| Version | Changes |
| --------- | ----------------------------- |
| `v16.x.0` | `useOffline` hook introduced. |
---
title: useOffline
description: Learn how to enable the experimental `useOffline` configuration option to detect connectivity and retry failed requests automatically.
version: experimental
related:
links:
- app/api-reference/functions/use-offline
- app/guides/progressive-web-apps
---
The `useOffline` configuration option enables offline connectivity detection and automatic retry of failed navigation, prefetch, and Server Action requests. When enabled, it also exposes the [`useOffline`](/docs/app/api-reference/functions/use-offline) hook for reading the current offline state from Client Components.
```ts filename="next.config.ts" switcher
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
useOffline: true,
},
}
export default nextConfig
```
```js filename="next.config.js" switcher
module.exports = {
experimental: {
useOffline: true,
},
}
```
When enabled, Next.js will:
- Listen for the browser's [`offline`](https://developer.mozilla.org/en-US/docs/Web/API/Window/offline_event) and [`online`](https://developer.mozilla.org/en-US/docs/Web/API/Window/online_event) events to track connectivity.
- Detect network failures on navigation, prefetch, and Server Action requests.
- Poll for connectivity using [`HEAD`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods/HEAD) requests with backoff while offline.
- Automatically retry blocked requests once connectivity is restored.
- Make the [`useOffline`](/docs/app/api-reference/functions/use-offline) hook available from `next/offline`.
## How retry works
The offline state is entered through one of two paths:
- **Browser event.** Next.js registers a `window.addEventListener('offline', ...)` listener. When the OS reports the network interface as down, the offline state flips on immediately.
- **Failed fetch.** Any navigation, prefetch, or Server Action request whose `fetch()` rejects with a non-abort, non-timeout error calls into the offline module. This catches the case where the browser still reports `navigator.onLine === true` but the actual request cannot reach the origin (captive portal, broken DNS, dead upstream).
Once in the offline state, a polling loop tries to confirm that connectivity has returned.
### The connectivity check
Each check issues a single `HEAD` request to the current page's URL with the RSC header set, the same endpoint navigations use. The request is aborted after 200 ms.
Two outcomes count as "online":
1. The fetch resolves normally.
2. The 200 ms timeout aborts the request. A truly offline request fails almost instantly (DNS or TCP error), so if it's still pending at 200 ms the TCP handshake succeeded and the server is reachable.
Any other rejection schedules the next check. A successful framework fetch (navigation, prefetch, Server Action) during the offline period also flips the state back to online.
### Backoff
Delays between checks are stepped, not exponential, and capped at 3 seconds:
| Attempt | Delay before next check |
| ----------- | ----------------------- |
| 1 | 500 ms |
| 2 | 1 s |
| 3 | 2 s |
| 4 and after | 3 s |
The browser's `online` event short-circuits the current wait and runs a connectivity check immediately. Reconnection is detected without waiting for the next scheduled tick.
### Giving up
The polling loop never gives up on its own. It continues at the 3-second cap until a check succeeds or the page unloads. A device that goes offline for hours and then regains connectivity will have its polling loop resume and resolve normally.
### Retry of framework requests
While the offline state is active, any navigation, prefetch, or Server Action waits for the next connectivity check to succeed, whether it was newly issued or already in flight when the connection dropped. When the check succeeds, the request runs once; no extra backoff applies.
If it fails with a network error, the app re-enters the offline state and the polling loop starts again.
### Traffic at reconnection
A single client does not produce a runaway burst of traffic against its origin:
- While the client is offline, a failed `fetch()` rejects locally at the browser's network layer. The request never reaches the origin.
- The polling loop issues one `HEAD` request at a time, with delays capped at 3 seconds. No other framework requests are sent to the origin during the offline period.
- When connectivity returns, each pending navigation and Server Action fires once. Only the last navigation attempt is kept pending, and a typical form button is disabled while its action is pending.
- Prefetches run through the [existing prefetch queue](/docs/app/guides/prefetching#prefetch-scheduling), not all at once.
In practice, this feature is unlikely to flood your server. The only extra traffic it generates per offline user is the HEAD polling, which stops as soon as connectivity returns. Prefetches, Server Actions, and navigations fire the same number of times they would have without an outage, just delayed.
## Version History
| Version | Changes |
| --------- | ---------------------------------------------------------- |
| `v16.x.0` | `experimental.useOffline` configuration option introduced. |
+1
-1

@@ -138,3 +138,3 @@ "use strict";

}({});
const nextVersion = "16.3.0-canary.101";
const nextVersion = "16.3.0-canary.102";
const ArchName = (0, _os.arch)();

@@ -141,0 +141,0 @@ const PlatformName = (0, _os.platform)();

@@ -96,3 +96,3 @@ "use strict";

isPersistentCachingEnabled: persistentCaching,
nextVersion: "16.3.0-canary.101"
nextVersion: "16.3.0-canary.102"
}, {

@@ -99,0 +99,0 @@ turbopackMemoryEviction: config.experimental.turbopackMemoryEvictionMode,

@@ -118,3 +118,3 @@ // Import cpu-profile first to start profiling early if enabled

deferredEntries: config.experimental.deferredEntries,
nextVersion: "16.3.0-canary.101"
nextVersion: "16.3.0-canary.102"
};

@@ -121,0 +121,0 @@ const sharedTurboOptions = {

@@ -42,3 +42,3 @@ #!/usr/bin/env node

const nextBuild = async (options, directory)=>{
process.title = `next-build (v${"16.3.0-canary.101"})`;
process.title = `next-build (v${"16.3.0-canary.102"})`;
process.on('SIGTERM', ()=>{

@@ -45,0 +45,0 @@ (0, _cpuprofile.saveCpuProfile)();

@@ -43,3 +43,3 @@ #!/usr/bin/env node

const bindings = await (0, _swc.loadBindings)((_config_experimental1 = config.experimental) == null ? void 0 : _config_experimental1.useWasmBinary);
await bindings.turbo.databaseCompact(cachePath, "16.3.0-canary.101");
await bindings.turbo.databaseCompact(cachePath, "16.3.0-canary.102");
console.log('Turbopack database compaction complete.');

@@ -46,0 +46,0 @@ };

@@ -18,3 +18,3 @@ /**

const _setattributesfromprops = require("./set-attributes-from-props");
const version = "16.3.0-canary.101";
const version = "16.3.0-canary.102";
window.next = {

@@ -21,0 +21,0 @@ version,

@@ -63,3 +63,3 @@ /* global location */ // imports polyfill from `@next/polyfill-module` after build.

const _isnextroutererror = require("./components/is-next-router-error");
const version = "16.3.0-canary.101";
const version = "16.3.0-canary.102";
let router;

@@ -66,0 +66,0 @@ const emitter = (0, _mitt.default)();

@@ -263,2 +263,4 @@ ---

> **Good to know**: An **experimental** [`useOffline`](/docs/app/api-reference/config/next-config-js/useOffline) hook can keep prefetched routes navigable during connectivity drops. See the [offline support guide](/docs/app/guides/offline-support).
> **Good to know**: You can use other visual feedback patterns like a progress bar. View an example [here](https://github.com/vercel/react-transition-progress).

@@ -265,0 +267,0 @@

@@ -379,2 +379,4 @@ ---

> **Good to know**: With the **experimental** [`useOffline`](/docs/app/guides/offline-support) config enabled, a Server Action interrupted by a connectivity drop stays pending and completes when the network returns.
### Refresh data

@@ -381,0 +383,0 @@

@@ -382,2 +382,4 @@ ---

> **Good to know**: With the **experimental** [`useOffline`](/docs/app/guides/offline-support) config enabled, a Server Action interrupted by a connectivity drop stays pending and completes when the network returns, so a user does not lose their submission.
## Optimistic updates

@@ -384,0 +386,0 @@

@@ -77,2 +77,4 @@ ---

> **Good to know**: With the **experimental** [`useOffline`](/docs/app/guides/offline-support) config enabled, pending prefetches resume through this queue when the app recovers from a connectivity drop.
### Client cache

@@ -79,0 +81,0 @@

@@ -674,4 +674,4 @@ ---

2. **Static Exports:** If your application requires not running a server, and instead using a static export of files, you can update the Next.js configuration to enable this change. Learn more in the [Next.js Static Export documentation](/docs/app/guides/static-exports). However, you will need to move from Server Actions to calling an external API, as well as moving your defined headers to your proxy.
3. **Offline Support**: To provide offline functionality, one option is [Serwist](https://github.com/serwist/serwist) with Next.js. You can find an example of how to integrate Serwist with Next.js in their [documentation](https://github.com/serwist/serwist/tree/main/examples/next-basic). **Note:** this plugin currently requires webpack configuration.
3. **Offline Support**: Next.js provides an experimental [`useOffline`](/docs/app/api-reference/functions/use-offline) hook and matching [`experimental.useOffline`](/docs/app/api-reference/config/next-config-js/useOffline) config for connectivity-aware UI and automatic retry of failed navigation and Server Action requests. For full service-worker-based offline caching, one option is [Serwist](https://github.com/serwist/serwist) with Next.js. You can find an example of how to integrate Serwist with Next.js in their [documentation](https://github.com/serwist/serwist/tree/main/examples/next-basic). **Note:** this plugin currently requires webpack configuration.
4. **Security Considerations**: Ensure that your service worker is properly secured. This includes using HTTPS, validating the source of push messages, and implementing proper error handling.
5. **User Experience**: Consider implementing progressive enhancement techniques to ensure your app works well even when certain PWA features are not supported by the user's browser.

@@ -14,3 +14,3 @@ import path from 'path';

}({});
const nextVersion = "16.3.0-canary.101";
const nextVersion = "16.3.0-canary.102";
const ArchName = arch();

@@ -17,0 +17,0 @@ const PlatformName = platform();

@@ -69,3 +69,3 @@ import path from 'path';

isPersistentCachingEnabled: persistentCaching,
nextVersion: "16.3.0-canary.101"
nextVersion: "16.3.0-canary.102"
}, {

@@ -72,0 +72,0 @@ turbopackMemoryEviction: config.experimental.turbopackMemoryEvictionMode,

@@ -87,3 +87,3 @@ // Import cpu-profile first to start profiling early if enabled

deferredEntries: config.experimental.deferredEntries,
nextVersion: "16.3.0-canary.101"
nextVersion: "16.3.0-canary.102"
};

@@ -90,0 +90,0 @@ const sharedTurboOptions = {

@@ -8,3 +8,3 @@ /**

import { setAttributesFromProps } from './set-attributes-from-props';
const version = "16.3.0-canary.101";
const version = "16.3.0-canary.102";
window.next = {

@@ -11,0 +11,0 @@ version,

@@ -28,3 +28,3 @@ /* global location */ // imports polyfill from `@next/polyfill-module` after build.

import { isNextRouterError } from './components/is-next-router-error';
export const version = "16.3.0-canary.101";
export const version = "16.3.0-canary.102";
export let router;

@@ -31,0 +31,0 @@ export const emitter = mitt();

@@ -20,3 +20,3 @@ import { readFileSync, writeFileSync } from 'fs';

const data = await res.json();
const versionData = data.versions["16.3.0-canary.101"];
const versionData = data.versions["16.3.0-canary.102"];
return {

@@ -54,3 +54,3 @@ os: versionData.os,

lockfileParsed.dependencies[pkg] = {
version: "16.3.0-canary.101",
version: "16.3.0-canary.102",
resolved: pkgData.tarball,

@@ -63,3 +63,3 @@ integrity: pkgData.integrity,

lockfileParsed.packages[pkg] = {
version: "16.3.0-canary.101",
version: "16.3.0-canary.102",
resolved: pkgData.tarball,

@@ -66,0 +66,0 @@ integrity: pkgData.integrity,

@@ -14,3 +14,3 @@ import { loadEnvConfig } from '@next/env';

const versionSuffix = logBundler ? ` (${bundlerName(getBundlerFromEnv())})` : '';
Log.bootstrap(`${bold(purple(`${Log.prefixes.ready} Next.js ${"16.3.0-canary.101"}`))}${versionSuffix}`);
Log.bootstrap(`${bold(purple(`${Log.prefixes.ready} Next.js ${"16.3.0-canary.102"}`))}${versionSuffix}`);
if (appUrl) {

@@ -17,0 +17,0 @@ Log.bootstrap(`- Local: ${appUrl}`);

@@ -112,3 +112,3 @@ // Start CPU profile if it wasn't already started.

let { port } = serverOptions;
process.title = `next-server (v${"16.3.0-canary.101"})`;
process.title = `next-server (v${"16.3.0-canary.102"})`;
let handlersReady = ()=>{};

@@ -115,0 +115,0 @@ let handlersError = ()=>{};

export function isStableBuild() {
return !"16.3.0-canary.101"?.includes('canary') && !process.env.__NEXT_TEST_MODE && !process.env.NEXT_PRIVATE_LOCAL_DEV;
return !"16.3.0-canary.102"?.includes('canary') && !process.env.__NEXT_TEST_MODE && !process.env.NEXT_PRIVATE_LOCAL_DEV;
}

@@ -4,0 +4,0 @@ export class CanaryOnlyConfigError extends Error {

@@ -75,3 +75,3 @@ "use strict";

const data = await res.json();
const versionData = data.versions["16.3.0-canary.101"];
const versionData = data.versions["16.3.0-canary.102"];
return {

@@ -104,3 +104,3 @@ os: versionData.os,

lockfileParsed.dependencies[pkg] = {
version: "16.3.0-canary.101",
version: "16.3.0-canary.102",
resolved: pkgData.tarball,

@@ -113,3 +113,3 @@ integrity: pkgData.integrity,

lockfileParsed.packages[pkg] = {
version: "16.3.0-canary.101",
version: "16.3.0-canary.102",
resolved: pkgData.tarball,

@@ -116,0 +116,0 @@ integrity: pkgData.integrity,

@@ -82,3 +82,3 @@ "use strict";

const versionSuffix = logBundler ? ` (${(0, _bundler.bundlerName)((0, _bundler.getBundlerFromEnv)())})` : '';
_log.bootstrap(`${(0, _picocolors.bold)((0, _picocolors.purple)(`${_log.prefixes.ready} Next.js ${"16.3.0-canary.101"}`))}${versionSuffix}`);
_log.bootstrap(`${(0, _picocolors.bold)((0, _picocolors.purple)(`${_log.prefixes.ready} Next.js ${"16.3.0-canary.102"}`))}${versionSuffix}`);
if (appUrl) {

@@ -85,0 +85,0 @@ _log.bootstrap(`- Local: ${appUrl}`);

@@ -180,3 +180,3 @@ // Start CPU profile if it wasn't already started.

let { port } = serverOptions;
process.title = `next-server (v${"16.3.0-canary.101"})`;
process.title = `next-server (v${"16.3.0-canary.102"})`;
let handlersReady = ()=>{};

@@ -183,0 +183,0 @@ let handlersError = ()=>{};

@@ -24,3 +24,3 @@ "use strict";

function isStableBuild() {
return !"16.3.0-canary.101"?.includes('canary') && !process.env.__NEXT_TEST_MODE && !process.env.NEXT_PRIVATE_LOCAL_DEV;
return !"16.3.0-canary.102"?.includes('canary') && !process.env.__NEXT_TEST_MODE && !process.env.NEXT_PRIVATE_LOCAL_DEV;
}

@@ -27,0 +27,0 @@ class CanaryOnlyConfigError extends Error {

@@ -85,3 +85,3 @@ "use strict";

ciName: _ciinfo.isCI && _ciinfo.name || null,
nextVersion: "16.3.0-canary.101",
nextVersion: "16.3.0-canary.102",
agentName: await (0, _agentname.getAgentName)()

@@ -88,0 +88,0 @@ };

@@ -14,7 +14,7 @@ "use strict";

// This should be an invariant, if it fails our build tooling is broken.
if (typeof "16.3.0-canary.101" !== 'string') {
if (typeof "16.3.0-canary.102" !== 'string') {
return [];
}
const payload = {
nextVersion: "16.3.0-canary.101",
nextVersion: "16.3.0-canary.102",
nodeVersion: process.version,

@@ -21,0 +21,0 @@ cliCommand: event.cliCommand,

@@ -41,3 +41,3 @@ "use strict";

payload: {
nextVersion: "16.3.0-canary.101",
nextVersion: "16.3.0-canary.102",
glibcVersion,

@@ -44,0 +44,0 @@ installedSwcPackages,

@@ -15,3 +15,3 @@ "use strict";

// This should be an invariant, if it fails our build tooling is broken.
if (typeof "16.3.0-canary.101" !== 'string') {
if (typeof "16.3.0-canary.102" !== 'string') {
return [];

@@ -21,3 +21,3 @@ }

const payload = {
nextVersion: "16.3.0-canary.101",
nextVersion: "16.3.0-canary.102",
nodeVersion: process.version,

@@ -24,0 +24,0 @@ cliCommand: event.cliCommand,

{
"name": "next",
"version": "16.3.0-canary.101",
"version": "16.3.0-canary.102",
"description": "The React Framework",

@@ -84,3 +84,3 @@ "main": "./dist/server/next.js",

"dependencies": {
"@next/env": "16.3.0-canary.101",
"@next/env": "16.3.0-canary.102",
"@swc/helpers": "0.5.15",

@@ -116,10 +116,10 @@ "baseline-browser-mapping": "^2.9.19",

"sharp": "^0.35.3",
"@next/swc-darwin-arm64": "16.3.0-canary.101",
"@next/swc-darwin-x64": "16.3.0-canary.101",
"@next/swc-linux-arm64-gnu": "16.3.0-canary.101",
"@next/swc-linux-arm64-musl": "16.3.0-canary.101",
"@next/swc-linux-x64-gnu": "16.3.0-canary.101",
"@next/swc-linux-x64-musl": "16.3.0-canary.101",
"@next/swc-win32-arm64-msvc": "16.3.0-canary.101",
"@next/swc-win32-x64-msvc": "16.3.0-canary.101"
"@next/swc-darwin-arm64": "16.3.0-canary.102",
"@next/swc-darwin-x64": "16.3.0-canary.102",
"@next/swc-linux-arm64-gnu": "16.3.0-canary.102",
"@next/swc-linux-arm64-musl": "16.3.0-canary.102",
"@next/swc-linux-x64-gnu": "16.3.0-canary.102",
"@next/swc-linux-x64-musl": "16.3.0-canary.102",
"@next/swc-win32-arm64-msvc": "16.3.0-canary.102",
"@next/swc-win32-x64-msvc": "16.3.0-canary.102"
},

@@ -126,0 +126,0 @@ "keywords": [

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display