Navigation
Programmatic Navigation
The router exposes navigation methods directly. Import the router and call them.
push
Adds a new entry to browser history.
import { router } from './router'
router.push('/projects/42')
router.push({
path: '/projects',
query: { sort: 'name' },
hash: 'details',
})replace
Replaces the current history entry. The back button skips over it.
router.replace('/login')
router.replace({
path: '/dashboard',
query: { tab: 'overview' },
})navigate
Alias for push. Use whichever reads better in context.
router.navigate('/projects/42')back, forward, go
router.back() // go back one entry
router.forward() // go forward one entry
router.go(-2) // go back two entriesThese map directly to history.back(), history.forward(), and history.go().
Recommendations
- Use
pushfor user-initiated navigation (clicking a button, submitting a form). - Use
replacefor redirects and programmatic corrections (auth redirects, fixing stale URLs). - Avoid
go()with large offsets. Users lose context when they jump multiple entries.
Link Component
Link renders an <a> tag with the correct href. It intercepts left-clicks and calls router.push (or router.replace with the replace prop). Modifier-key clicks (ctrl, meta, shift, alt), non-left-button clicks, and external URLs (http://, https://) pass through to the browser.
import { Link } from '@geajs/core'
<Link to="/dashboard">Dashboard</Link>Props
| Prop | Type | Description |
|---|---|---|
to | string | Target path (required) |
label | string | Text content (alternative to children) |
children | string | Inner HTML: <Link to="/about">About</Link> |
class | string | CSS class(es) |
replace | boolean | Use router.replace() instead of router.push() |
target | string | Link target (e.g. _blank) |
rel | string | Link relationship (e.g. noopener) |
onNavigate | (e: MouseEvent) => void | Callback fired before SPA navigation |
replace
Use the replace prop to call router.replace instead of router.push.
<Link to="/login" replace>Sign in</Link>External links
Use target and rel for links that should open in a new tab:
<Link to="https://docs.example.com" target="_blank" rel="noopener">Docs</Link>Styling
Link is a plain <a> tag. Pass class for styling.
<Link to="/about" class="nav-link">About</Link>Recommendations
- Use
Linkinstead of<a>for internal navigation. It prevents full page reloads. - Use plain
<a>for external links.Linkis for routes within your app.
Active Link Detection
The router provides two methods for detecting which route is active.
isActive
Returns true if the current path starts with the given path. Use it for parent navigation items that should stay highlighted when a child route is active.
router.isActive('/dashboard') // true for /dashboard, /dashboard/projects, /dashboard/projects/42isExact
Returns true only on an exact path match.
router.isExact('/dashboard') // true only for /dashboardExample: Active Nav Links
import { Component } from '@geajs/core'
import { Link } from '@geajs/core'
import { router } from './router'
import { cn } from './utils/cn'
export default class Nav extends Component {
template() {
return (
<nav>
<Link to="/dashboard"
class={cn('nav-link', router.isExact('/dashboard') && 'active')}>
Overview
</Link>
<Link to="/dashboard/projects"
class={cn('nav-link', router.isActive('/dashboard/projects') && 'active')}>
Projects
</Link>
<Link to="/settings"
class={cn('nav-link', router.isActive('/settings') && 'active')}>
Settings
</Link>
</nav>
)
}
}Recommendations
- Use
isExactfor top-level items like "Overview" that shouldn't stay highlighted when a sibling is active. - Use
isActivefor section-level items like "Projects" that should stay highlighted for all child routes (/projects,/projects/42,/projects/42/edit). - Keep active detection in the template. It's reactive — the classes update automatically when the route changes.
