Next.js Router
The @nano_kit/next-router package provides Next.js integration for @nano_kit/router. It re-exports everything from @nano_kit/react-router and adds Next.js-specific navigation providers, a router-aware Link, and helpers for the Pages Router.
For shared router APIs such as useLocation, useNavigation, usePaths, useListenLinks, see the React Router integration.
Installation
Section titled “Installation”Install the package using your favorite package manager:
pnpm add @nano_kit/store @nano_kit/router @nano_kit/react @nano_kit/next-routeryarn add @nano_kit/store @nano_kit/router @nano_kit/react @nano_kit/next-routernpm install @nano_kit/store @nano_kit/router @nano_kit/react @nano_kit/next-routerLike @nano_kit/react-router, this package works with route definitions declared in AppContext.routes.
import { routes } from '@/stores/router'
declare module '@nano_kit/router' { interface AppContext { routes: typeof routes }}The Next.js setup differs slightly between the App Router and the Pages Router.
App Router
Section titled “App Router”Use NextNavigation in the root layout to create navigation for the current RSC request and provide Location$ / Navigation$ to client components.
import type { Metadata } from 'next'import { FlightDetector, HydrationProvider} from '@nano_kit/react'import { NextNavigation } from '@nano_kit/next-router'import { routes } from '@/stores/router'
declare module '@nano_kit/router' { interface AppContext { routes: typeof routes }}
export const metadata: Metadata = { title: 'My App'}
export default function RootLayout({ children }: { children: React.ReactNode}) { return ( <FlightDetector> <NextNavigation routes={routes} prerenderable > <HydrationProvider> <html lang='en'> <body>{children}</body> </html> </HydrationProvider> </NextNavigation> </FlightDetector> )}If a page dehydrates stores on the server, wrap that page’s dehydration boundary in its own NextNavigation. This ensures client-side flight renders still have a current navigation context.
import { Dehydration } from '@nano_kit/react'import { NextNavigation } from '@nano_kit/next-router'import { routes } from '@/stores/router'import CharactersPage, { Stores$ } from '@/ui/pages/Characters'
export default function Page() { return ( <NextNavigation routes={routes}> <Dehydration stores={Stores$}> <CharactersPage /> </Dehydration> </NextNavigation> )}Pages Router
Section titled “Pages Router”Use NextNavigationProvider in _app.tsx to provide navigation in client components, then hydrate store data through HydrationProvider.
import type { AppProps } from 'next/app'import { HydrationProvider } from '@nano_kit/react'import { NextNavigationProvider } from '@nano_kit/next-router'import { routes } from '@/stores/router'
declare module '@nano_kit/router' { interface AppContext { routes: typeof routes }}
export default function App({ Component, pageProps }: AppProps) { return ( <NextNavigationProvider routes={routes}> <HydrationProvider dehydrated={pageProps.dehydrated}> <Component {...pageProps} /> </HydrationProvider> </NextNavigationProvider> )}For SSR data loading in getServerSideProps, use virtualNavigationContext together with dehydrate.
import type { GetServerSideProps } from 'next'import { dehydrate } from '@nano_kit/store'import { virtualNavigationContext } from '@nano_kit/next-router'import { routes } from '@/stores/router'import CharactersPage, { Stores$ } from '@/ui/pages/Characters'
export const getServerSideProps: GetServerSideProps = async (context) => { const dehydrated = await dehydrate( Stores$, virtualNavigationContext(context.resolvedUrl, routes) )
return { props: { dehydrated } }}
export default function Page() { return <CharactersPage />}Next Specifics
Section titled “Next Specifics”NextNavigation
Section titled “NextNavigation”NextNavigation is an async RSC component for the App Router. It creates navigation for the current request, provides Location$ and Navigation$, and renders NextNavigationProvider on the client.
Server-side navigation.push() and navigation.replace() are mapped to Next.js redirects. The prerenderable prop skips Next.js connection() and allows the route to stay statically prerenderable, but in that mode searchParams are not available during server render.
import { NextNavigation } from '@nano_kit/next-router'
export default function Layout({ children }: { children: React.ReactNode}) { return ( <NextNavigation routes={routes} prerenderable > {children} </NextNavigation> )}NextNavigationProvider
Section titled “NextNavigationProvider”NextNavigationProvider is the client-side provider used by the Pages Router and by NextNavigation after hydration. It creates an InjectionContext with Location$ and Navigation$ only when a fresh client navigation context is needed.
import { NextNavigationProvider } from '@nano_kit/next-router'
<NextNavigationProvider routes={routes}> <App /></NextNavigationProvider>Link is a typed wrapper around next/link. It supports route-aware to and params props, but also accepts plain href when needed.
Use it inside NextNavigation or NextNavigationProvider.
'use client'import { Link } from '@nano_kit/next-router'
export function Nav() { return ( <nav> <Link to='characters'>Characters</Link> <Link to='character' params={{ id: '42' }}> Rick </Link> <Link href='/about'>About</Link> </nav> )}redirect & notFound
Section titled “redirect & notFound”These helpers are for the Pages Router.
redirect(context, permanent?)returns a Next.js{ redirect: ... }if redirection was triggered during dehydration.notFound(value)returns{ notFound: true }when the value is falsy.
import type { GetServerSideProps } from 'next'import { contextDehydrate } from '@nano_kit/store'import { notFound, redirect, virtualNavigationContext} from '@nano_kit/next-router'import { routes } from '@/stores/router'import { User$ } from '@/stores/user'import UserPage, { Stores$ } from '@/ui/pages/User'
export const getServerSideProps: GetServerSideProps = async (nextContext) => { const [context, dehydrated] = await contextDehydrate( Stores$, virtualNavigationContext(nextContext.resolvedUrl, routes) ) const { $user } = context.get(User$)
return notFound($user()) ?? redirect(context) ?? { props: { dehydrated } }}
export default function Page() { return <UserPage />}