Guide & Best Practices
Router works best when the URL is treated as the source of truth for navigation, filters, tabs, and shareable UI state. Keep route declarations close to the layout they control, then use the JavaScript API only for behavior that cannot be expressed cleanly in HTML.
Start With Declarative Routes
For most pages, use <page-route>, <page-link>, and <page-redirect> directly in the document. This keeps the app understandable from the HTML structure alone.
1<nav>2 <page-link path="/" title="Home">Home</page-link>3 <page-link path="/projects" title="Projects">Projects</page-link>4 <page-link path="/settings" title="Settings">Settings</page-link>5</nav>6 7<main>8 <page-route path="/" src="./pages/home.html"></page-route>9 <page-route10 path="/projects"11 exact="false"12 src="./pages/projects.js"13 ></page-route>14 <page-route path="/settings" src="./pages/settings.html"></page-route>15 <page-route path="/404">Page not found.</page-route>16 <page-redirect path="/404"></page-redirect>17</main>Use Nested Routes For Layouts
Parent routes are useful for sections that share navigation, headings, or route data. Child paths extend the nearest parent route.
1<page-route path="/projects/:projectId" exact="false">2 <header>3 <h1>Project <page-data param="projectId">unknown</page-data></h1>4 <page-link path="$/overview">Overview</page-link>5 <page-link path="$/activity">Activity</page-link>6 <page-link path="$/settings">Settings</page-link>7 </header>8 9 <page-route path="/overview" src="./projects/overview.js"></page-route>10 <page-route path="/activity" src="./projects/activity.html"></page-route>11 <page-route path="/settings" src="./projects/settings.js"></page-route>12 <page-redirect path="$/overview" type="always"></page-redirect>13</page-route>Use Query Routes For UI State
Use <page-route-query> when the view is still the same page but a query value should choose a tab, drawer, filter, or modal.
1<page-link search="panel=details" keep-current-search>Details</page-link>2<page-link search="panel=activity" keep-current-search>Activity</page-link>3 4<page-route-query key="panel" value="details">5 <h2>Details</h2>6</page-route-query>7 8<page-route-query key="panel" value="activity" src="./panels/activity.html">9 <p slot="loading">Loading activity...</p>10</page-route-query>Prefer component For Typed Apps
If you are rendering routes from Markup or Web Component code, the component property avoids string-based dynamic imports and gives bundlers a direct reference.
1import { html } from '@beforesemicolon/web-component'2import HomePage from './pages/HomePage.js'3import ProjectPage from './pages/ProjectPage.js'4 5export const routes = html`6 <page-route path="/" component="${HomePage}"></page-route>7 <page-route8 path="/projects/:projectId"9 component="${ProjectPage}"10 ></page-route>11`Register Guards Before Navigation
Guards run before route listeners and route components are notified. Register them during application startup, before rendering protected routes or triggering navigation.
1import {2 registerGlobalGuard,3 registerRouteGuard,4} from '@beforesemicolon/router'5 6registerGlobalGuard((pathname) => {7 if (pathname.startsWith('/account') && !session.currentUser) {8 return '/login'9 }10 11 return true12})13 14registerRouteGuard('/admin/:section', async () => {15 return (await session.hasRole('admin')) || '/unauthorized'16})Keep 404s Last
<page-redirect> checks the routes that have been registered. Put fallback redirects after the valid <page-route> declarations they depend on.
1<page-route path="/">Home</page-route>2<page-route path="/docs" exact="false">Docs</page-route>3<page-route path="/404">Not found</page-route>4 5<page-redirect path="/404"></page-redirect>Choose History Or Hash Routing Early
History routing gives clean URLs and is the default. Hash routing is useful when your host cannot rewrite unknown paths back to index.html.
1import { setRoutingMode } from '@beforesemicolon/router'2 3setRoutingMode('hash')Production Checklist
- Give every meaningful route a document
titlethrough<page-link>,<page-route>, or your ownonPageChangehandler. - Use route params for resource identity and search params for shareable view state.
- Keep route modules small and lazy-load secondary screens.
- Use
nameon overlapping route groups so only the first matching route renders. - Use
payloadonly for transient navigation state. Persist important state in the URL or application data store.