Navigation
routekit gives you three ways to change URLs: native <a> clicks (intercepted automatically), programmatic router.go(), and direct history.pushState if you want to bypass routekit’s hooks.
Link interception
When you call router.start(), routekit attaches a single delegated click handler at document. Any in-app <a href="..."> click that:
- has a same-origin URL,
- is not a download (
target="_blank",download,[rel="external"]), - is not a modified click (cmd-click, middle-click),
is intercepted: routekit calls event.preventDefault() and router.go(href) instead. Native browser behavior is preserved otherwise.
<!-- Just write normal HTML; routekit handles the rest --><a href="/posts/42">Read post 42</a><a href="/posts.csv" download>Export</a> <!-- bypassed, native --><a href="https://example.com">External</a> <!-- bypassed, native --><a href="/edit" target="_blank">Edit (new tab)</a> <!-- bypassed, native -->Programmatic navigation
router.go('/posts/42'); // pushes a new history entryrouter.replace('/login'); // replaces current history entryrouter.back(); // history.back()router.forward(); // history.forward()go() and replace() accept either a path string or an object:
router.go('/posts/42');router.go({ path: '/posts/42', state: { from: 'newsletter' } });The state is exposed on subsequent match.state reads. Use this for back-button-friendly flows (e.g. preserving scroll position, modal open/close state).
Active-link styling
There’s no <NavLink> component — instead, attach a CSS class to your <a> element and let the browser do the work:
import { isActive } from 'routekit';
document.querySelectorAll('a[href]').forEach((a) => { a.toggleAttribute('data-active', isActive(a.getAttribute('href')!, router.url));});Then style with [data-active]:
nav a[data-active] { color: var(--accent); font-weight: 600;}isActive(href, currentUrl) returns true when:
- exact match (
/users→/users), OR - prefix match with a trailing slash (
/users/→/users/42).
If you want stricter matching (exact only), pass { exact: true }:
isActive('/users', router.url, { exact: true });Handling external links
Cross-origin <a> clicks are not intercepted — they trigger normal browser navigation away from your app. If you want to confirm before leaving (for unsaved-form prompts, e.g.), use the standard beforeunload event:
window.addEventListener('beforeunload', (e) => { if (formIsDirty) { e.preventDefault(); e.returnValue = ''; }});routekit deliberately doesn’t expose a “before-navigate” hook. The platform already has one.
Scroll restoration
routekit doesn’t manage scroll. By default, router.go() does NOT scroll to the top — your app keeps its current scroll position. To match the typical SPA expectation:
router.on('navigate', ({ trigger }) => { if (trigger === 'push') { // Forward navigation: scroll to top. window.scrollTo({ top: 0, behavior: 'instant' }); } // 'pop' (back/forward button): browser handles scroll restoration.});The trigger field disambiguates pushed vs. popped navigations so you only scroll on forward moves.