Router API Reference
On this page 28
This document details stx's routing system API, including route configuration, navigation guards, and route management.
Page metadata on navigation
STX fragment responses include an inert <template data-stx-page-head="1">
payload. On navigation, the router synchronizes the destination title,
description, Open Graph (og:*) and Twitter (twitter:*) metadata, canonical
link, and locale alternate links (rel="alternate" with hreflang). Prefetch
does not change the active head; cached and retained back/forward visits restore
the destination metadata. Supported tags missing from the new payload are removed.
This is not a whole-head replacement. Scripts, stylesheets, preload links,
security tokens, theme metadata, and unrelated head state are outside this
contract. Only the supported tags' semantic attributes are copied. Older
clients leave the template inert; a new client receiving an older server's
fragment without a payload leaves its existing metadata unchanged. Full-document
SPA swaps use the same metadata allowlist. Full browser navigations are unchanged.
Authored numeric and named HTML entities are preserved for a single browser
decode, including literal entity text such as &lt;.
The parsed payload title takes precedence over the legacy X-STX-Title
header; that header remains the fallback for older fragment responses.
Router Setup
Basic Configuration
import { createRouter } from '@stacksjs/stx/router'
const router = createRouter({
// Base URL for all routes
base: '/',
// Route definitions
routes: [
{
path: '/',
component: Home
},
{
path: '/about',
component: About
},
{
path: '/users/:id',
component: UserProfile,
props: true
}
],
// Scroll behavior
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
return { top: 0 }
}
})
Route Configuration
interface RouteConfig {
// Route path
path: string
// Route name (optional)
name?: string
// Component to render
component: Component
// Pass route params as props
props?: boolean | Object | Function
// Nested routes
children?: RouteConfig[]
// Route metadata
meta?: Record<string, any>
// Route alias
alias?: string | string[]
// Redirect configuration
redirect?: string | Location | Function
// Route-specific navigation guards
beforeEnter?: NavigationGuard
// Lazy loading
component: () => import('./UserProfile.stx')
}
Route Definition
Basic Routes
const routes = [
// Static routes
{
path: '/',
component: Home
},
// Dynamic segments
{
path: '/users/:id',
component: UserProfile
},
// Optional parameters
{
path: '/posts/:id?',
component: PostView
},
// Multiple dynamic segments
{
path: '/orgs/:orgId/repos/:repoId',
component: RepoView
},
// Catch all / 404
{
path: '/:pathMatch(.*)*',
component: NotFound
}
]
Nested Routes
const routes = [
{
path: '/user',
component: User,
children: [
{
// /user/profile
path: 'profile',
component: UserProfile
},
{
// /user/posts
path: 'posts',
component: UserPosts
},
{
// /user/settings
path: 'settings',
component: UserSettings
}
]
}
]
Named Routes
const routes = [
{
path: '/user/:id',
name: 'user',
component: User
}
]
// Navigate using route name
router.push({ name: 'user', params: { id: '123' } })
Navigation Guards
Global Guards
// Before each route change
router.beforeEach((to, from) => {
// Check authentication
if (to.meta.requiresAuth && !isAuthenticated) {
return '/login'
}
})
// After each route change
router.afterEach((to, from) => {
// Analytics tracking
trackPageView(to.fullPath)
})
// Route error handling
router.onError((error) => {
// Handle navigation errors
console.error('Navigation error:', error)
})
Route-Specific Guards
const routes = [
{
path: '/admin',
component: Admin,
beforeEnter: (to, from) => {
// Check admin access
if (!hasAdminAccess) {
return '/unauthorized'
}
}
}
]
Component Guards
const AdminDashboard = defineComponent({
setup() {
// Called before component is created
onBeforeRouteEnter((to, from) => {
// No access to 'this'
})
// Called before route changes
onBeforeRouteUpdate((to, from) => {
// Can access component instance
})
// Called before leaving route
onBeforeRouteLeave((to, from) => {
// Prevent accidental navigation
const answer = window.confirm('Leave page?')
if (!answer) return false
})
}
})
Navigation Methods
Programmatic Navigation
// Push new route
router.push('/about')
// Push with object
router.push({
path: '/users',
query: { page: 2 }
})
// Push with name
router.push({
name: 'user',
params: { id: '123' }
})
// Replace current route
router.replace('/other')
// Go back/forward
router.go(-1) // back
router.go(1) // forward
// Push with hash
router.push({
path: '/about',
hash: '#team'
})
Route Information
// Current route
const route = router.currentRoute.value
// Access route data
console.log(route.path) // Current path
console.log(route.params) // Route parameters
console.log(route.query) // Query parameters
console.log(route.hash) // URL hash
console.log(route.fullPath) // Full URL
console.log(route.name) // Route name
console.log(route.meta) // Route metadata
// Check current route
router.isReady() // Router initialized
router.hasRoute('name') // Route exists
Route Composition
Route Utilities
import { useRoute, useRouter } from '@stacksjs/stx/router'
// In component setup
const route = useRoute()
const router = useRouter()
// Access route info
console.log(route.params.id)
console.log(route.query.page)
// Navigate
router.push('/new-path')
Route Guards Composition
import { onBeforeRouteLeave, onBeforeRouteUpdate } from '@stacksjs/stx/router'
// In component setup
onBeforeRouteLeave((to, from) => {
const answer = window.confirm('Leave page?')
if (!answer) return false
})
onBeforeRouteUpdate((to, from) => {
// Handle route updates
})
Advanced Features
Route Meta Fields
const routes = [
{
path: '/admin',
component: Admin,
meta: {
requiresAuth: true,
roles: ['admin'],
title: 'Admin Dashboard'
}
}
]
router.beforeEach((to, from) => {
// Check meta requirements
if (to.meta.requiresAuth) {
// Auth check
}
if (to.meta.roles) {
// Role check
}
// Update page title
document.title = to.meta.title
})
Dynamic Routes
// Lazy loading
const routes = [
{
path: '/user/:id',
component: () => import('./views/User.stx')
}
]
// Dynamic route matching
const routes = [
// Match /user-1, /user-2, etc.
{
path: '/user-:id(\\d+)',
component: UserById
},
// Optional parameters
{
path: '/optional/:id?',
component: OptionalId
},
// Custom regex
{
path: '/:id(\\d+)',
component: NumberId
}
]
Navigation Failures
// Handle navigation failures
router.push('/path').catch(failure => {
if (failure.type === NavigationFailureType.aborted) {
// Navigation aborted
}
if (failure.type === NavigationFailureType.cancelled) {
// Navigation cancelled
}
})
// Check navigation status
const navigation = await router.push('/path')
if (navigation.aborted) {
// Handle aborted navigation
}
Navigation Composables
STX provides auto-imported navigation functions that replace raw window.location and window.history usage. These work in <script client> blocks without any imports.
navigate(url)
Navigate to a URL. Uses the SPA router (window.stxRouter) when available, otherwise falls back to window.location.href.
// In <script client> — auto-imported
navigate('/about')
navigate('/users/123')
navigate('https://external.com')
<template
<buttonclick="">Dashboard</button>
</template>
goBack() / goForward()
<template
<buttonclick="">Back</button>
<buttonclick="">Forward</button>
</template>
useRoute()
Reactive route information. Returns getters that always reflect the current URL.
const route = useRoute()
route.path // '/about'
route.fullPath // '/about?page=2#section'
route.hash // '#section'
route.query // { page: '2' }
route.params // {} (from stxRouter if available)
useSearchParams()
Reactive URL search parameters. Automatically syncs with popstate and stx:navigate events.
const search = useSearchParams()
search.get('page') // '2'
search.has('page') // true
search.set('page', '3') // Updates the URL, pushes a history entry
search.setAll({ page: '1', sort: 'name' }) // Set multiple params
search.delete('page') // Remove one
Every mutator takes an optional { replace: true }, which replaces the current
history entry instead of pushing a new one:
// Consuming a one-shot param — an OAuth callback, ?checkout=success, a flash
// token. Without `replace` the URL still carrying `code` stays in history, so
// Back replays the callback.
search.delete('code', { replace: true })
Next Steps
- Explore Core API
- Check out Component API
- Learn about Helper Functions
- Review State Management