Documentation Index
Fetch the complete documentation index at: https://mintlify.com/reatom/reatom/llms.txt
Use this file to discover all available pages before exploring further.
Overview
reatomRoute creates a reactive route atom that automatically syncs with the browser URL. Routes provide:
- URL pattern matching with parameters
- Type-safe navigation
- Schema validation for parameters
- Async data loading with automatic abort
- Component rendering with outlet composition
- Hierarchical route nesting
Type Signature
function reatomRoute<
Path extends string = '',
ParamsInput = PathParams<Path>,
SearchInput = {},
ParamsOutput = ParamsInput,
SearchOutput = SearchInput,
>(
pathOrOptions: string | RouteOptions<Path, ParamsInput, SearchInput, ParamsOutput, SearchOutput>,
name?: string
): RouteAtom<Path, ParamsOutput, SearchOutput>
Parameters
Either a path pattern string (e.g.,
'users/:userId') or a configuration objectShow RouteOptions properties
Show RouteOptions properties
Path pattern with
:paramName for required parameters and :paramName? for optional ones.
Examples: 'users', 'users/:userId', 'posts/:postId?'Schema or function to validate and transform path parameters.
URL params are always strings, so schemas should accept strings and transform to desired types.
Schema to validate and transform search/query parameters.
All search parameters should be optional in the schema.
Async function to load data when route becomes active.
Receives validated parameters (path + search combined).
Automatically aborted when navigating away.
Function to render route component. Receives the route atom with all properties.
Used for framework-agnostic component composition.
Only render when URL exactly matches this route (not partial matches)
Debug name for the route atom
Return Value
Route atom that returns validated params when matched, or
null when not matched.Show properties
Show properties
Navigate to this route with given parameters.
params- Route parameters (path + search)replace- If true, replaces current history entry
Build URL path string without navigating. Useful for creating links.
Async data loader with:
loader()- Promise that resolves to loaded dataloader.data()- Last successful resultloader.error()- Last errorloader.pending()- Number of pending loadsloader.ready()- Boolean indicating data is readyloader.retry()- Retry failed load
Whether current URL matches this route (partial or exact)
Whether current URL exactly matches this route
The path pattern string for this route
Registry of all child routes created from this route
Array of rendered child route components
Rendered component for this route, or null
Create a child route by appending a path pattern
Parent route in the hierarchy
Examples
Basic Routes
import { reatomRoute } from '@reatom/framework'
const homeRoute = reatomRoute('')
const aboutRoute = reatomRoute('about')
const userRoute = reatomRoute('users/:userId')
// Navigate
homeRoute.go()
aboutRoute.go()
userRoute.go({ userId: '123' })
// Check if matched
console.log(homeRoute()) // {} when at /
console.log(userRoute()) // { userId: '123' } when at /users/123
console.log(userRoute()) // null when not matched
// Build URLs
console.log(aboutRoute.path()) // '/about'
console.log(userRoute.path({ userId: '456' })) // '/users/456'
Optional Parameters
import { reatomRoute } from '@reatom/framework'
const postRoute = reatomRoute('posts/:postId?')
// Both paths match
postRoute.go() // /posts
postRoute.go({ postId: 'abc' }) // /posts/abc
console.log(postRoute()) // {} at /posts
console.log(postRoute()) // { postId: 'abc' } at /posts/abc
Nested Routes
import { reatomRoute } from '@reatom/framework'
const apiRoute = reatomRoute('api')
const productsRoute = apiRoute.reatomRoute('products')
const productRoute = productsRoute.reatomRoute(':productId')
const settingsRoute = productRoute.reatomRoute('settings')
// Navigate to nested route
settingsRoute.go({ productId: 'abc' })
// URL: /api/products/abc/settings
// Check matches at each level
console.log(apiRoute()) // {}
console.log(productsRoute()) // {}
console.log(productRoute()) // { productId: 'abc' }
console.log(settingsRoute()) // { productId: 'abc' }
// Check exact matches
console.log(apiRoute.exact()) // false
console.log(settingsRoute.exact()) // true
Parameter Validation
import { reatomRoute } from '@reatom/framework'
import { z } from 'zod'
const issueRoute = reatomRoute({
path: 'issues/:issueId',
params: z.object({
issueId: z.string().regex(/^\d+$/).transform(Number),
}),
})
// Type-safe navigation
issueRoute.go({ issueId: '123' })
// Params are validated and transformed
console.log(issueRoute()) // { issueId: 123 } (number, not string)
// Invalid params are rejected
issueRoute.go({ issueId: 'abc' }) // Throws error
Search Parameters
import { reatomRoute } from '@reatom/framework'
import { z } from 'zod'
const searchRoute = reatomRoute({
path: 'search',
search: z.object({
q: z.string().optional(),
page: z.string().transform(Number).default('1'),
sort: z.enum(['asc', 'desc']).optional(),
}),
})
searchRoute.go({ q: 'reatom', page: 2, sort: 'desc' })
// URL: /search?q=reatom&page=2&sort=desc
console.log(searchRoute())
// { q: 'reatom', page: 2, sort: 'desc' }
Data Loading
import { reatomRoute } from '@reatom/framework'
import { z } from 'zod'
const userRoute = reatomRoute({
path: 'users/:userId',
params: z.object({
userId: z.string().regex(/^\d+$/).transform(Number),
}),
async loader({ userId }) {
const response = await fetch(`/api/users/${userId}`)
return response.json()
},
})
userRoute.go({ userId: '123' })
// Access loader state
const user = await wrap(userRoute.loader())
console.log(userRoute.loader.data()) // User data
console.log(userRoute.loader.ready()) // true when loaded
console.log(userRoute.loader.error()) // Error if failed
// Loader automatically aborts when navigating away
userRoute.go({ userId: '456' }) // Previous load is aborted
Nested Loaders
import { reatomRoute } from '@reatom/framework'
const organizationRoute = reatomRoute({
path: 'orgs/:orgId',
async loader({ orgId }) {
const org = await fetch(`/api/orgs/${orgId}`).then(r => r.json())
return org
},
})
const projectRoute = organizationRoute.reatomRoute({
path: 'projects/:projectId',
async loader({ orgId, projectId }) {
// Parent loader waits first
const project = await fetch(
`/api/orgs/${orgId}/projects/${projectId}`
).then(r => r.json())
return project
},
})
// Parent loader executes before child
projectRoute.go({ orgId: '1', projectId: '2' })
// 1. organizationRoute.loader executes
// 2. projectRoute.loader executes after parent completes
Search-Only Routes
import { reatomRoute } from '@reatom/framework'
import { z } from 'zod'
// Route with no path - only manages search params
const dialogRoute = reatomRoute({
search: z.object({
dialog: z.enum(['login', 'signup']).optional(),
}),
})
// Preserves current pathname, only updates search
urlAtom.go('/profile/123')
dialogRoute.go({ dialog: 'login' })
// URL: /profile/123?dialog=login
urlAtom.go('/settings')
dialogRoute.go({ dialog: 'signup' })
// URL: /settings?dialog=signup
Component Rendering
import { reatomRoute } from '@reatom/framework'
import { html } from 'lit'
const layoutRoute = reatomRoute({
render(self) {
// self() returns params when matched
// self.outlet() returns child route components
return html`
<div>
<header>My App</header>
<main>${self.outlet().map((child) => child)}</main>
</div>
`
},
})
const homeRoute = layoutRoute.reatomRoute({
path: 'home',
render() {
return html`<h1>Home Page</h1>`
},
})
const aboutRoute = layoutRoute.reatomRoute({
path: 'about',
render() {
return html`<h1>About Page</h1>`
},
})
// Access rendered components
console.log(layoutRoute.render()) // Layout component
console.log(layoutRoute.outlet()) // [homeRoute or aboutRoute component]
Exact Rendering
import { reatomRoute } from '@reatom/framework'
const projectRoute = reatomRoute({
path: 'project',
exactRender: true,
render() {
return html`<h1>Project Overview</h1>`
},
})
const settingsRoute = projectRoute.reatomRoute({
path: 'settings',
render() {
return html`<h1>Project Settings</h1>`
},
})
// At /project
console.log(projectRoute.render()) // Project overview component
// At /project/settings
console.log(projectRoute.render()) // null (not exact match)
console.log(settingsRoute.render()) // Settings component
Callback-Based Params
import { reatomRoute } from '@reatom/framework'
// Custom validation logic
const dialogRoute = reatomRoute({
params: ({ open }: { open: boolean }) => ({ open }),
})
const profileRoute = dialogRoute.reatomRoute({
params: ({ profileOf }: { profileOf: string }) =>
profileOf ? { profileOf } : null,
})
profileRoute.go({ open: true, profileOf: 'user123' })
console.log(dialogRoute()) // { open: true }
console.log(profileRoute()) // { profileOf: 'user123' }
profileRoute.go({ open: true, profileOf: '' })
console.log(profileRoute()) // null (validation failed)
404 Detection
import { reatomRoute, is404 } from '@reatom/framework'
const homeRoute = reatomRoute('')
const aboutRoute = reatomRoute('about')
// Navigate to unknown path
urlAtom.go('/unknown')
console.log(is404()) // true
console.log(homeRoute()) // null
console.log(aboutRoute()) // null
// Navigate to known path
homeRoute.go()
console.log(is404()) // false
Loader Pending State
import { reatomRoute, isSomeLoaderPending } from '@reatom/framework'
const route1 = reatomRoute({
path: 'route1',
async loader() {
await sleep(1000)
return {}
},
})
const route2 = reatomRoute({
path: 'route2',
async loader() {
await sleep(500)
return {}
},
})
route1.go()
console.log(isSomeLoaderPending()) // true
await wrap(route1.loader())
console.log(isSomeLoaderPending()) // false
Type Utilities
PathParams
Extract parameter types from a path pattern:import { type PathParams } from '@reatom/framework'
type Params1 = PathParams<'users/:userId/posts/:postId?'>
// { userId: string; postId?: string }
type Params2 = PathParams<':id'>
// { id: string }
Related
- searchParamsAtom - Global search parameters atom
- urlAtom - Global URL atom
- Standard Schema - Schema validation specification