Routing
Navigate between views with PACE.js's hash-based router.
Overview
PACE.js uses hash-based routing for single-page navigation:
/#product → Product catalog
/#about → About page
/#chat → Chat widget
/#summary → Executive summary
/#product/sql-mcp → Product detailBasic Navigation
Programmatic Navigation
javascript
import { PACE } from '@semanticintent/pace-pattern'
const pace = new PACE({ ... })
pace.mount()
// Navigate to views
pace.navigate('product')
pace.navigate('about')
pace.navigate('chat')
pace.navigate('summary')
// Navigate with parameters
pace.navigate('product', { id: 'sql-mcp' })Link Navigation
html
<a href="#product">Products</a>
<a href="#about">About</a>
<a href="#chat">Chat</a>
<a href="#summary">Summary</a>
<a href="#product/sql-mcp">SQL MCP</a>Router API
Initialize
javascript
import { Router, State } from '@semanticintent/pace-pattern'
const state = new State()
const router = new Router(state)
router.init()Navigate
javascript
router.navigate('product')
router.navigate('product', { id: 'sql-mcp' })Listen to Route Changes
javascript
router.on('navigate', (route, params) => {
console.log('Navigated to:', route, params)
})
// Listen to all routes
router.on('*', (route, params) => {
console.log('Route change:', route, params)
})
// Listen to specific route
router.on('product', (params) => {
console.log('Product view:', params)
})Route Parameters
URL Parameters
/#product/sql-mcpjavascript
router.on('product', ({ id }) => {
console.log('Product ID:', id) // 'sql-mcp'
loadProduct(id)
})Query Parameters
/#product?category=databases&sort=popularjavascript
router.on('product', ({ category, sort }) => {
console.log('Category:', category) // 'databases'
console.log('Sort:', sort) // 'popular'
filterProducts({ category, sort })
})Deep Linking
Share specific views:
javascript
// Share product link
const productUrl = `${window.location.origin}/#product/${product.id}`
navigator.clipboard.writeText(productUrl)
// Share chat with context
const chatUrl = `${window.location.origin}/#chat?topic=databases`
// Share summary
const summaryUrl = `${window.location.origin}/#summary`Route Guards
Authentication Guard
javascript
router.beforeNavigate((to, from) => {
if (to === 'admin' && !isAuthenticated()) {
router.navigate('login')
return false // Cancel navigation
}
return true // Allow navigation
})Confirmation Guard
javascript
router.beforeNavigate((to, from) => {
if (from === 'chat' && hasUnsavedMessages()) {
return confirm('You have unsaved messages. Leave anyway?')
}
return true
})Custom Routes
Add Custom Routes
javascript
// Add admin route
router.addRoute('admin', () => {
state.set('activeView', 'admin')
renderAdminPanel()
})
// Add settings route
router.addRoute('settings', ({ tab }) => {
state.set('activeView', 'settings')
state.set('settingsTab', tab || 'general')
renderSettings()
})
// Usage
router.navigate('admin')
router.navigate('settings', { tab: 'theme' })Route Transitions
Animated Transitions
javascript
router.on('navigate', async (to, from) => {
// Fade out current view
await fadeOut(getCurrentView())
// Update state
state.set('activeView', to)
// Fade in new view
await fadeIn(getNewView())
})Loading States
javascript
router.on('navigate', async (to) => {
// Show loading
state.set('loading', true)
// Load data if needed
if (to === 'product') {
await loadProducts()
}
// Hide loading
state.set('loading', false)
})Browser History
Back/Forward
PACE.js automatically handles browser back/forward:
javascript
// User clicks browser back button
// Router automatically navigates to previous routeProgrammatic History
javascript
// Go back
window.history.back()
// Go forward
window.history.forward()
// Go to specific point
window.history.go(-2)Scroll Behavior
Scroll to Top
javascript
router.on('navigate', () => {
window.scrollTo({ top: 0, behavior: 'smooth' })
})Preserve Scroll Position
javascript
const scrollPositions = new Map()
router.on('navigate', (to, from) => {
// Save current scroll position
if (from) {
scrollPositions.set(from, window.scrollY)
}
// Restore scroll position
const savedPosition = scrollPositions.get(to)
if (savedPosition) {
window.scrollTo({ top: savedPosition })
} else {
window.scrollTo({ top: 0 })
}
})Analytics Integration
Track Page Views
javascript
router.on('navigate', (route, params) => {
gtag('config', 'GA_MEASUREMENT_ID', {
page_path: `/#${route}`,
page_title: getPageTitle(route)
})
})Track Route Timing
javascript
let routeStartTime
router.on('navigate', (route) => {
if (routeStartTime) {
const duration = Date.now() - routeStartTime
gtag('event', 'timing_complete', {
name: 'route_transition',
value: duration,
event_category: 'navigation'
})
}
routeStartTime = Date.now()
})Server-Side Rendering (SSR)
PACE.js uses hash-based routing, which works without SSR. But if you need SSR:
Convert to History API
javascript
class HistoryRouter extends Router {
init() {
window.addEventListener('popstate', () => {
this.handleRoute(window.location.pathname)
})
}
navigate(route, params) {
const url = this.buildUrl(route, params)
window.history.pushState({}, '', url)
this.handleRoute(route, params)
}
}Server Configuration
javascript
// Express.js
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'index.html'))
})
// Nginx
location / {
try_files $uri $uri/ /index.html;
}Nested Routes
javascript
// Define nested routes
router.addRoute('admin/users', () => {
renderAdminUsers()
})
router.addRoute('admin/settings', () => {
renderAdminSettings()
})
// Navigate
router.navigate('admin/users')
router.navigate('admin/settings')Route Metadata
javascript
const routes = new Map([
['product', { title: 'Products', requiresAuth: false }],
['admin', { title: 'Admin Panel', requiresAuth: true }],
['settings', { title: 'Settings', requiresAuth: true }]
])
router.on('navigate', (route) => {
const meta = routes.get(route)
// Update page title
document.title = `${meta.title} | PACE App`
// Check auth
if (meta.requiresAuth && !isAuthenticated()) {
router.navigate('login')
}
})Best Practices
1. Use Hash Routes for Static Hosting
✅ Hash routes work on GitHub Pages, Netlify, Vercel without config
2. Always Handle 404
javascript
router.on('404', () => {
state.set('activeView', 'not-found')
})3. Validate Route Parameters
javascript
router.on('product', ({ id }) => {
if (!id || !products.find(p => p.id === id)) {
router.navigate('product')
return
}
loadProduct(id)
})4. Use Route Constants
javascript
const ROUTES = {
PRODUCT: 'product',
ABOUT: 'about',
CHAT: 'chat',
SUMMARY: 'summary'
}
router.navigate(ROUTES.PRODUCT)Navigate seamlessly with PACE routing! 🧭