connection()
Pauses rendering until a real user request arrives, excluding the code after it from prerendering.
function connection(): Promise<void>- Added in Next.js v15.0.0-RC
Return value
Promise<void>A void Promise. It is not meant to be consumed — awaiting it is the whole point, since the await is what stops prerendering.
Changelog
- PHP v15.0.0connection stabilized.
- PHP v15.0.0-RCconnection introduced. It replaces unstable_noStore.
await connection() tells Next.js that rendering must wait for an incoming user request. During a prerender it never resolves, so everything after the await is excluded from the build output and runs only when a real visitor arrives.
When to reach for it
It exists for the case where a component produces per-request output without touching a request-time API. Math.random(), new Date(), crypto.randomUUID() and synchronous database drivers like better-sqlite3 all complete happily during a prerender, which is exactly the problem — the build would freeze one value into the HTML forever. If the component already calls cookies() or headers(), you do not need connection(); those are themselves the suspension point.
What trips people up
Because it waits for a genuine request, it also blocks prefetches — the router cannot warm that part of the tree ahead of a navigation, so the user pays for it on click. With Cache Components enabled, prefer io(): it excludes the same code from the static shell but suspends like any ordinary async call, so the result can still be cached and prefetched. Reach for connection() only when you truly need the request itself.
Example
import { connection } from 'next/server'
export default async function Page() {
await connection() // prerendering stops here
// Only runs at request time
const bucket = Math.random() < 0.5 ? 'control' : 'variant'
return <p>Experiment bucket: {bucket}</p>
}When to use it
- Keep a Math.random() A/B bucket out of the build output
- Read a synchronous SQLite driver per request instead of at build time
- Render a per-request timestamp that must not be frozen into static HTML
- Replace a legacy unstable_noStore call during a Next.js 15 upgrade
Frequently asked questions
What replaced unstable_noStore in Next.js 15?
connection() replaced it. Import it from next/server and await it before the code you want excluded from prerendering; it stabilized in v15.0.0 and aligns with how the App Router models request-time work.
Should I use connection() or io()?
Prefer io() when Cache Components is enabled. Both keep the following code out of the static shell, but connection() stays suspended until a full user navigation reaches the server and therefore blocks prefetches, while io() suspends like a normal async call and stays cacheable and prefetchable.
Do I need connection() if my component already calls cookies()?
No. Request-time APIs like cookies() and headers() are themselves the suspension point, so the component is already excluded from the prerender. connection() is only for components that vary per request without reading any request data.
More examples
A synchronous database driver
A synchronous driver returns immediately, so nothing else would stop the query running during next build and baking that row into the page. The await makes the whole call — and every component that renders its result — excluded from the prerender. This is the example the official docs lead with.
import { connection } from 'next/server'
import Database from 'better-sqlite3'
const db = new Database('app.db')
export async function getVisitorCount() {
await connection()
return db.prepare('SELECT value FROM counters WHERE name = ?').get('visitors')
}Scoping the cost to one subtree
Calling connection() at the top of Page would exclude the entire route from prerendering, heading included. Pushed into a suspended child, only LiveCounter waits for the request while the shell around it still ships statically.
import { Suspense } from 'react'
import { connection } from 'next/server'
export default function Page() {
return (
<main>
<h1>Dashboard</h1>
<Suspense fallback={<p>Loading live figures…</p>}>
<LiveCounter />
</Suspense>
</main>
)
}
async function LiveCounter() {
await connection()
const count = await getVisitorCount()
return <p>{JSON.stringify(count)}</p>
}Preferring io() under Cache Components
Same exclusion from the static shell, but io() suspends like any async function, so the surrounding work stays prefetchable and cacheable. Swapping connection() for io() is the right default with Cache Components enabled; keep connection() only where the semantics 'do not run until a person actually asks' are what you mean.
import { Suspense } from 'react'
import { io } from 'next/cache'
export default function Page() {
return (
<Suspense fallback={<p>Loading…</p>}>
<RequestId />
</Suspense>
)
}
async function RequestId() {
await io()
return <p>{crypto.randomUUID()}</p>
}
Discussion