Advanced
Code Splitting
Section titled “Code Splitting”Router provides built-in support for code splitting and lazy loading of pages using the loadable function.
loadable
Section titled “loadable”Defines a lazy-loaded component or page that is fetched only when needed. It accepts a module loader function (returning a promise) and an optional fallback view to display while loading.
The loaded module must export a default component/view and optionally a Stores$ function for data prefetching and dehydration in SSR.
import { router, loadable, page } from '@nano_kit/router'
/* Fallback component */const Loader = () => 'Loading...'
export const $page = router($location, [ page('home', loadable(() => import('./pages/Home.js'), Loader)), page('user', loadable(() => import('./pages/User.js'), Loader))])loadPage
Section titled “loadPage”Forces the loading of a specific page’s code by its route name. This is useful for preloading the next likely page (e.g., on link hover).
import { loadPage } from '@nano_kit/router'import { routes } from './routes.js'
/* Preload user page code */await loadPage(routes, 'user')loadPages
Section titled “loadPages”Loads the code for all defined pages in the route tree. This is commonly used in server-side rendering (SSR) to ensure all async components are resolved before rendering the application.
import { loadPages } from '@nano_kit/router'import { routes } from './routes.js'
/* Preload all pages */await loadPages(routes)Head Management
Section titled “Head Management”The router ships with a reactive head management API for controlling document metadata (title, meta tags, link tags, scripts) on a per-page basis.
Page Module Head$
Section titled “Page Module Head$”Add a Head$ factory to a page module to declare which head descriptors it provides. The router exposes it on the current $page() ref, where syncHead can read and apply it.
import { Location$, title, meta } from '@nano_kit/router'import { inject } from '@nano_kit/store'
export function Head$() { const $location = inject(Location$)
return [ title(() => `User ${$location().params.id}`), meta({ name: 'description', content: 'User profile page' }) ]}
export default function UserPage() { /* ... */ }syncHead
Section titled “syncHead”syncHead subscribes to the current page accessor and applies all head descriptors reactively. Call it once during app initialization.
import { syncHead } from '@nano_kit/router'
const $page = router($location, pages)
syncHead($page)You can pass an optional InjectionContext as the second argument to resolve Head$ factories with DI:
syncHead($page, context)Tag Descriptors
Section titled “Tag Descriptors”| Function | Updates |
|---|---|
title($value) | <title> in <head> |
lang($value) | <html lang> |
dir($value) | <html dir> |
link(props) | <link> in <head> |
meta(props) | <meta> in <head> |
script(props) | <script> in <head> |
title, lang, and dir accept a plain value or a reactive accessor. For link and meta, only certain props support reactive accessors — the ones that are likely to change at runtime:
link—href,media,disabled,titlemeta—content,media
import { title, lang, link, meta } from '@nano_kit/router'
export function Head$() { const $user = inject(User$)
return [ title(() => `${$user().name} — My App`), lang('en'), link({ rel: 'canonical', href: () => $user().profileUrl }), meta({ property: 'og:title', content: () => $user().name }) ]}Scroll Management
Section titled “Scroll Management”Since scroll restoration and behavior can vary significantly between applications, the library provides a set of “Do It Yourself” (DIY) utilities instead of a one-size-fits-all solution. You can combine these tools to implement the exact scrolling behavior your app needs.
resetScroll
Section titled “resetScroll”The router does not reset scroll automatically. Use resetScroll to move the window scroll position to the top (0, 0), for example when navigating to a new page.
import { onMountEffect } from '@nano_kit/store'import { resetScroll } from '@nano_kit/router'
const { $route } = $location
/* Reset scroll whenever the route changes */onMountEffect($route, () => { $route() resetScroll()})To skip scroll resets for specific navigations, you can use the navigation action as part of your own scroll policy. For example, this pattern resets scroll for push navigations but keeps the current scroll position for replace navigations:
import { onMountEffect } from '@nano_kit/store'import { ReplaceHistoryAction, resetScroll } from '@nano_kit/router'
const { $route, $action } = $location
/* Reset scroll on route changes, except replace navigations */onMountEffect($route, () => { $route()
if ($action() !== ReplaceHistoryAction) { resetScroll() }})
/* Keeps the current scroll position with the effect above */navigation.replace('/characters?page=2')scrollToAnchor
Section titled “scrollToAnchor”Scrolls the window to a specific element identified by a URL hash (e.g., #section). It handles looking up the element by ID or name and scrolling it into view.
import { onMountEffect } from '@nano_kit/store'import { scrollToAnchor } from '@nano_kit/router'
/* Scroll to element with id="features" smoothly */scrollToAnchor('#features', { behavior: 'smooth' })
const { $hash } = $location
/* Or automatically handle hash from location */onMountEffect($hash, () => { const hash = $hash()
if (hash) { scrollToAnchor(hash) }})ScrollRestorator
Section titled “ScrollRestorator”A utility class that saves and restores scroll positions using sessionStorage. It helps maintain the user’s scroll position when navigating back and forth within the history.
import { ScrollRestorator } from '@nano_kit/router'
/* 1. Create instance (optional prefix) */const scrollRestorator = new ScrollRestorator('my-app-scroll-')
/* 2. Save scroll position before leaving the current view *//* (e.g., in a cleanup function or before navigation) */scrollRestorator.save($location())
/* 3. Restore scroll position when returning to a view */const restored = scrollRestorator.restore($location())
if (!restored) { /* If no position was saved, default to top */ resetScroll()}Transitions
Section titled “Transitions”The transition method on the navigation object allows you to intercept and control the navigation process. This is powerful for implementing global behaviors like scroll restoration, page transitions, or navigation guards (confirmation dialogs).
By default, it simply executes the transition. You can override it to add custom logic.
import { browserNavigation } from '@nano_kit/router'
const [$location, navigation] = browserNavigation(routes)
/* Example 1: Scroll Restoration */navigation.transition = (proceed, nextLocation, prevLocation) => { /* Save scroll position for the page we are leaving */ scrollRestorator.save(prevLocation)
/* Proceed with the navigation */ proceed(nextLocation)
/* Restore scroll position for the new page */ scrollRestorator.restore(nextLocation)}
/* Example 2: Navigation Guard / Confirmation */navigation.transition = (proceed, nextLocation, prevLocation) => { const isDirty = formIsDirty()
if (!isDirty || confirm('Are you sure you want to leave? Unsaved changes will be lost.')) { proceed(nextLocation) }}The arguments are:
proceed: A function that must be called to complete the navigation. PassnextLocationto it.nextLocation: The target location object (ornullif unknown).prevLocation: The current location object before navigation.
basePath
Section titled “basePath”The basePath helper allows you to prefix all routes with a common base path. This is useful when your application is hosted in a subdirectory (e.g., GitHub Pages) or behind a specific path.
import { basePath } from '@nano_kit/router'
/* All routes will be prefixed with /admin */const routes = basePath('/admin', { dashboard: '/', users: '/users'})
/* Resulting patterns: *//* dashboard -> /admin/ *//* users -> /admin/users */updateHref
Section titled “updateHref”A utility to update parts of a URL string (pathname, search, or hash) while preserving the rest. It accepts a current href and an update object or string.
import { updateHref } from '@nano_kit/router'
const current = '/users?sort=name#top'
/* Update query params */const next = updateHref(current, { search: '?sort=date' })// /users?sort=date#top
/* Update path */const moved = updateHref(current, { pathname: '/admins' })// /admins?sort=name#topremoveTrailingSlash
Section titled “removeTrailingSlash”Removes the trailing slash from a path string, ensuring consistent path handling.
import { removeTrailingSlash } from '@nano_kit/router'
removeTrailingSlash('/path/') // '/path'removeTrailingSlash('/path') // '/path'onLinkClick
Section titled “onLinkClick”A helper function to handle click events on anchor tags (<a>) in a Single Page Application (SPA) way. It intercepts the click, prevents the default browser navigation, and instead calls navigation.push() or navigation.replace().
Use this if you need to build a custom Link component for your framework.
import { onLinkClick } from '@nano_kit/router'import { navigation } from './router.js'
const handleClick = onLinkClick.bind(navigation)
function CustomLink({ href, children }) { return <a href={href} onClick={handleClick}>{children}</a>}canGoBack
Section titled “canGoBack”canGoBack returns an Accessor<boolean> that is true when back navigation is possible (i.e. the history stack has more than one entry).
import { inject } from '@nano_kit/store'import { canGoBack } from '@nano_kit/router'
const [$location, navigation] = browserNavigation(routes)const $canGoBack = canGoBack($location, navigation)
effect(() => { console.log('Can go back:', $canGoBack())})/* Output: Can go back: false (initially) */
navigation.push('/users')/* Output: Can go back: true */Dependency Injection
Section titled “Dependency Injection”The router provides global injection tokens for navigation state. Use them in stores or components to access routing data without passing signals as props.
Tokens
Section titled “Tokens”Location$- provides the current location signal. Token itself doesn’t have a default value — it should be provided to the context by the app.Navigation$- provides the navigation API. LikeLocation$, it should be provided by the app.Page$- provides the current page reference from the router. Should be provided by the app.Pages$- provides the array of page definitions passed to the router. Should be provided by the app.Paths$- provides typed path builder functions derived from the navigation. Depends onNavigation$.CanGoBack$- provides a boolean signal indicating if back navigation is possible. Depends onLocation$andNavigation$.
Not every token needs to be provided — it depends on your app’s needs. For example, if you don’t use path builders, you can skip providing Paths$.
AppContext
Section titled “AppContext”The router uses TypeScript declaration merging to infer app-specific types across all tokens. Extend the AppContext interface in your project to enable typed params, paths, and components:
import type { routes } from './routes.js'import type { MyComponent } from './types.js'
declare module '@nano_kit/router' { interface AppContext { routes: typeof routes // types Location$, Navigation$, Paths$ component: MyComponent // types Page$, Pages$ }}routes— must betypeof yourRoutesObject. Enables types forLocation$,Navigation$, andPaths$tokens based on your route definitions.component— your framework’s component type (e.g.() => ReactNode). Enables typed$page().defaultinPage$.
If AppContext is not extended, all tokens fall back to generic types.