Navigation
In React you reach for <Link> from react-router or Next.js to get client-side navigation and usePathname() / useRouter() to read the current URL. Astro’s MPA model means plain HTML <a href> links work perfectly — no special component needed. For the current URL, use Astro.url (a standard URL object available in every .astro file).
Basic navigation
Section titled “Basic navigation”// React / Next.js — needs <Link> for client-side navimport Link from 'next/link';import { usePathname } from 'next/navigation';
export default function Nav() { const pathname = usePathname(); const links = ['/', '/about', '/blog'];
return ( <nav> {links.map((href) => ( <Link key={href} href={href} className={pathname === href ? 'active' : ''} > {href === '/' ? 'Home' : href.slice(1)} </Link> ))} </nav> );}---// src/components/Nav.astro// Astro.url is a native URL object — no hook neededconst pathname = Astro.url.pathname;const links = ['/', '/about', '/blog'];---<nav> {links.map((href) => ( <a href={href} class={pathname === href ? 'active' : ''} > {href === '/' ? 'Home' : href.slice(1)} </a> ))}</nav>Key points:
Astro.urlis a standardURLobject — use.pathname,.origin,.searchParams, etc.- Active-link detection is pure string comparison — no special hook required.
- Plain
<a href>triggers a full page request (MPA model). The browser handles the navigation natively.
Runnable example — current path display
Section titled “Runnable example — current path display”This self-contained page demonstrates Astro.url at work. Open it in StackBlitz and note that the current pathname is rendered server-side.
---
const url = Astro.url;
const links = ['/', '/about', '/blog', '/contact'];
---
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Navigation demo</title>
<style>
body { font-family: sans-serif; padding: 2rem; }
nav { display: flex; gap: 1rem; margin-bottom: 2rem; }
a { color: #6366f1; text-decoration: none; padding: 0.25rem 0.75rem;
border-radius: 4px; border: 1px solid #6366f1; }
a.active { background: #6366f1; color: #fff; }
.info { background: #f1f5f9; padding: 1rem; border-radius: 6px; }
code { background: #e2e8f0; padding: 0.1rem 0.4rem; border-radius: 3px; }
</style>
</head>
<body>
<nav>
{links.map((href) => (
<a href={href} class={url.pathname === href ? 'active' : ''}>{href}</a>
))}
</nav>
<div class="info">
<p><strong>Astro.url.pathname:</strong> <code>{url.pathname}</code></p>
<p><strong>Astro.url.origin:</strong> <code>{url.origin}</code></p>
</div>
<p>Click a link — the active state is set at build/render time, not in JS.</p>
</body>
</html>ClientRouter — SPA-like animations
Section titled “ClientRouter — SPA-like animations”Astro ships a built-in <ClientRouter /> component that wraps the browser’s View Transitions API. Adding it to your layout gives you smooth, animated page transitions without converting your site to an SPA.
// Next.js — page transitions need a library// e.g. framer-motion AnimatePresence or next-view-transitionsimport { ViewTransitions } from 'next-view-transitions';
export default function RootLayout({ children }) { return ( <html> <body> <ViewTransitions> {children} </ViewTransitions> </body> </html> );}---// src/layouts/Layout.astroimport { ClientRouter } from 'astro:transitions';---<html lang="en"> <head> <meta charset="utf-8" /> <title>My Site</title> <!-- Add this ONE line to get SPA-like transitions --> <ClientRouter /> </head> <body> <slot /> </body></html>With <ClientRouter /> Astro intercepts link clicks and uses the native browser View Transitions API to animate between pages. You keep the MPA mental model — no client-side router, no JavaScript bundle for routing logic.