0
# Navigation & Routing
1
2
Vue Router integration with programmatic navigation, route middleware, and layout management. Nuxt provides enhanced routing capabilities with file-based routing, nested layouts, and powerful navigation utilities.
3
4
> **Note**: This module uses Vue Router types. In a real application, these are available through Nuxt's auto-imports or can be imported from 'vue-router'.
5
6
## Capabilities
7
8
### Router Access
9
10
Access Vue Router instance and current route information.
11
12
```typescript { .api }
13
/**
14
* Get the Vue Router instance
15
* @returns Vue Router instance
16
*/
17
function useRouter(): Router;
18
19
/**
20
* Get the current route information
21
* @returns Current route object with params, query, meta, etc.
22
*/
23
function useRoute(): RouteLocationNormalizedLoaded;
24
25
interface Router {
26
/** Navigate to a route */
27
push(to: RouteLocationRaw): Promise<NavigationFailure | void | undefined>;
28
/** Replace current route */
29
replace(to: RouteLocationRaw): Promise<NavigationFailure | void | undefined>;
30
/** Go back in history */
31
back(): void;
32
/** Go forward in history */
33
forward(): void;
34
/** Go to specific history position */
35
go(delta: number): void;
36
/** Add route dynamically */
37
addRoute(route: RouteRecordRaw): () => void;
38
/** Remove route by name */
39
removeRoute(name: string | symbol): void;
40
/** Check if route exists */
41
hasRoute(name: string | symbol): boolean;
42
/** Get all routes */
43
getRoutes(): RouteRecord[];
44
/** Resolve route location */
45
resolve(to: RouteLocationRaw): RouteLocation & { href: string };
46
/** Current route */
47
currentRoute: Readonly<Ref<RouteLocationNormalizedLoaded>>;
48
/** Navigation guards */
49
beforeEach(guard: NavigationGuardWithThis<undefined>): () => void;
50
beforeResolve(guard: NavigationGuardWithThis<undefined>): () => void;
51
afterEach(guard: NavigationHookAfter): () => void;
52
}
53
54
interface RouteLocationNormalizedLoaded {
55
/** Route name */
56
name?: string | symbol;
57
/** Route path */
58
path: string;
59
/** Route parameters */
60
params: RouteParams;
61
/** Query parameters */
62
query: LocationQuery;
63
/** Hash fragment */
64
hash: string;
65
/** Route meta information */
66
meta: RouteMeta;
67
/** Full URL including domain */
68
fullPath: string;
69
/** Matched route records */
70
matched: RouteRecordNormalized[];
71
/** Redirected from route */
72
redirectedFrom?: RouteLocation;
73
}
74
```
75
76
**Usage Examples:**
77
78
```typescript
79
// Get router and route
80
const router = useRouter();
81
const route = useRoute();
82
83
// Access route information
84
console.log("Current path:", route.path);
85
console.log("Route params:", route.params);
86
console.log("Query params:", route.query);
87
console.log("Route name:", route.name);
88
89
// Router navigation
90
await router.push("/users");
91
await router.push({ name: "users-id", params: { id: "123" } });
92
await router.replace("/home");
93
94
// History navigation
95
router.back();
96
router.forward();
97
router.go(-2);
98
99
// Dynamic routes
100
router.addRoute({
101
name: "admin",
102
path: "/admin/:section",
103
component: AdminComponent,
104
meta: { requiresAuth: true }
105
});
106
107
// Watch route changes
108
watch(route, (to, from) => {
109
console.log(`Navigated from ${from.path} to ${to.path}`);
110
});
111
112
// Reactive route parameters
113
const userId = computed(() => route.params.id);
114
watch(userId, (newId) => {
115
if (newId) {
116
fetchUser(newId);
117
}
118
});
119
```
120
121
### Programmatic Navigation
122
123
Navigate programmatically with enhanced features and error handling.
124
125
```typescript { .api }
126
/**
127
* Navigate to a route programmatically
128
* @param to - Route location to navigate to
129
* @param options - Navigation options
130
* @returns Navigation result or redirect response
131
*/
132
function navigateTo(
133
to: RouteLocationRaw,
134
options?: NavigateToOptions
135
): Promise<void | NavigationFailure | false> | false | void;
136
137
/**
138
* Abort the current navigation
139
* @param err - Optional error information
140
*/
141
function abortNavigation(err?: string | Partial<NuxtError>): void;
142
143
interface NavigateToOptions {
144
/** Replace current route instead of pushing */
145
replace?: boolean;
146
/** HTTP redirect code for SSR */
147
redirectCode?: number;
148
/** Navigate to external URL */
149
external?: boolean;
150
/** Open in new window/tab */
151
open?: {
152
target: string;
153
windowFeatures?: Record<string, any>;
154
};
155
}
156
```
157
158
**Usage Examples:**
159
160
```typescript
161
// Basic navigation
162
await navigateTo("/users");
163
164
// With parameters
165
await navigateTo(`/users/${userId}`);
166
await navigateTo({ name: "users-id", params: { id: userId } });
167
168
// With query parameters
169
await navigateTo({
170
path: "/search",
171
query: { q: "nuxt", category: "framework" }
172
});
173
174
// Replace current route
175
await navigateTo("/home", { replace: true });
176
177
// External navigation
178
await navigateTo("https://nuxt.com", { external: true });
179
180
// Open in new tab
181
await navigateTo("/docs", {
182
open: {
183
target: "_blank",
184
windowFeatures: {
185
width: 1200,
186
height: 800
187
}
188
}
189
});
190
191
// Server-side redirects
192
await navigateTo("/login", { redirectCode: 302 });
193
194
// Conditional navigation
195
if (!user.value) {
196
await navigateTo("/login");
197
}
198
199
// Navigation with error handling
200
try {
201
await navigateTo("/protected-route");
202
} catch (error) {
203
console.error("Navigation failed:", error);
204
await navigateTo("/error");
205
}
206
207
// Abort navigation
208
function onBeforeLeave() {
209
if (hasUnsavedChanges.value) {
210
abortNavigation("You have unsaved changes");
211
}
212
}
213
```
214
215
### Route Middleware
216
217
Add and manage route middleware for authentication, authorization, and other route-level logic.
218
219
```typescript { .api }
220
/**
221
* Add route middleware dynamically
222
* @param name - Middleware name or function
223
* @param middleware - Middleware function if name is string
224
* @param options - Middleware options
225
*/
226
function addRouteMiddleware(
227
name: string | RouteMiddleware,
228
middleware?: RouteMiddleware,
229
options?: AddRouteMiddlewareOptions
230
): void;
231
232
/**
233
* Define route middleware with proper typing
234
* @param middleware - Middleware function
235
* @returns The middleware function
236
*/
237
function defineNuxtRouteMiddleware(middleware: RouteMiddleware): RouteMiddleware;
238
239
type RouteMiddleware = (
240
to: RouteLocationNormalized,
241
from: RouteLocationNormalized
242
) => NavigationGuardReturn | Promise<NavigationGuardReturn>;
243
244
interface AddRouteMiddlewareOptions {
245
/** Whether middleware is global */
246
global?: boolean;
247
}
248
249
type NavigationGuardReturn =
250
| void
251
| Error
252
| RouteLocationRaw
253
| boolean
254
| NavigationGuardNextCallback;
255
```
256
257
**Usage Examples:**
258
259
```typescript
260
// Define middleware inline
261
addRouteMiddleware((to) => {
262
if (to.path === "/admin" && !user.value?.isAdmin) {
263
return navigateTo("/login");
264
}
265
});
266
267
// Define named middleware
268
addRouteMiddleware("auth", (to) => {
269
if (!user.value) {
270
return navigateTo("/login");
271
}
272
});
273
274
// Global middleware
275
addRouteMiddleware("track-analytics", (to) => {
276
analytics.page(to.path);
277
}, { global: true });
278
279
// Middleware in separate file (middleware/auth.ts)
280
export default defineNuxtRouteMiddleware((to) => {
281
const { user } = useAuth();
282
283
if (!user.value) {
284
throw createError({
285
statusCode: 401,
286
statusMessage: "Authentication required"
287
});
288
}
289
});
290
291
// Complex middleware with async operations
292
defineNuxtRouteMiddleware(async (to) => {
293
const { user, refresh } = useAuth();
294
295
// Refresh user data if needed
296
if (!user.value) {
297
await refresh();
298
}
299
300
// Check permissions
301
const hasPermission = await checkUserPermission(to.meta.permission);
302
if (!hasPermission) {
303
throw createError({
304
statusCode: 403,
305
statusMessage: "Insufficient permissions"
306
});
307
}
308
});
309
310
// Middleware with redirect
311
defineNuxtRouteMiddleware((to) => {
312
const { user } = useAuth();
313
314
if (to.path.startsWith("/admin") && user.value?.role !== "admin") {
315
return navigateTo("/dashboard");
316
}
317
318
if (to.path === "/profile" && !user.value?.emailVerified) {
319
return navigateTo("/verify-email");
320
}
321
});
322
```
323
324
### Navigation Guards
325
326
Set up navigation guards for component-level route handling.
327
328
```typescript { .api }
329
/**
330
* Add route leave guard
331
* @param guard - Navigation guard function
332
*/
333
function onBeforeRouteLeave(guard: NavigationGuard): void;
334
335
/**
336
* Add route update guard
337
* @param guard - Navigation guard function
338
*/
339
function onBeforeRouteUpdate(guard: NavigationGuard): void;
340
341
type NavigationGuard = (
342
to: RouteLocationNormalized,
343
from: RouteLocationNormalized,
344
next: NavigationGuardNext
345
) => NavigationGuardReturn | Promise<NavigationGuardReturn>;
346
```
347
348
**Usage Examples:**
349
350
```typescript
351
// Prevent leaving with unsaved changes
352
onBeforeRouteLeave((to, from, next) => {
353
if (hasUnsavedChanges.value) {
354
const answer = confirm("You have unsaved changes. Do you really want to leave?");
355
if (answer) {
356
next();
357
} else {
358
next(false);
359
}
360
} else {
361
next();
362
}
363
});
364
365
// Handle route parameter changes
366
onBeforeRouteUpdate(async (to, from) => {
367
if (to.params.id !== from.params.id) {
368
await fetchUser(to.params.id);
369
}
370
});
371
372
// Save scroll position
373
onBeforeRouteLeave(() => {
374
const scrollPosition = window.scrollY;
375
sessionStorage.setItem(`scroll-${route.path}`, scrollPosition.toString());
376
});
377
```
378
379
### Layout Management
380
381
Manage page layouts dynamically.
382
383
```typescript { .api }
384
/**
385
* Set the layout for the current page
386
* @param layout - Layout name to use
387
*/
388
function setPageLayout(layout: string): void;
389
```
390
391
**Usage Examples:**
392
393
```typescript
394
// Set layout based on user role
395
const { user } = useAuth();
396
watch(user, (newUser) => {
397
if (newUser?.role === "admin") {
398
setPageLayout("admin");
399
} else {
400
setPageLayout("default");
401
}
402
});
403
404
// Conditional layout
405
if (route.path.startsWith("/dashboard")) {
406
setPageLayout("dashboard");
407
}
408
409
// Mobile vs desktop layout
410
const isMobile = useMediaQuery("(max-width: 768px)");
411
watch(isMobile, (mobile) => {
412
setPageLayout(mobile ? "mobile" : "desktop");
413
});
414
```
415
416
### Advanced Navigation Patterns
417
418
```typescript
419
// Breadcrumb navigation
420
const breadcrumbs = computed(() => {
421
const matched = route.matched.filter(record => record.meta?.breadcrumb);
422
return matched.map(record => ({
423
name: record.meta.breadcrumb,
424
path: record.path,
425
params: route.params
426
}));
427
});
428
429
// Tab navigation with query sync
430
const activeTab = computed({
431
get: () => route.query.tab || "overview",
432
set: (tab) => {
433
router.push({
434
query: { ...route.query, tab }
435
});
436
}
437
});
438
439
// Search with history
440
const searchQuery = ref("");
441
const searchHistory = useState<string[]>("search-history", () => []);
442
443
const performSearch = async (query: string) => {
444
// Add to history
445
if (query && !searchHistory.value.includes(query)) {
446
searchHistory.value.unshift(query);
447
searchHistory.value = searchHistory.value.slice(0, 10); // Keep last 10
448
}
449
450
// Navigate with query
451
await navigateTo({
452
path: "/search",
453
query: { q: query }
454
});
455
};
456
457
// Route-based modal
458
const showModal = computed(() => route.query.modal === "true");
459
460
const openModal = () => {
461
router.push({
462
query: { ...route.query, modal: "true" }
463
});
464
};
465
466
const closeModal = () => {
467
const { modal, ...query } = route.query;
468
router.push({ query });
469
};
470
471
// Multi-step form navigation
472
const currentStep = computed(() =>
473
parseInt(route.params.step as string) || 1
474
);
475
476
const nextStep = () => {
477
const next = currentStep.value + 1;
478
router.push({
479
name: "form-step",
480
params: { step: next.toString() }
481
});
482
};
483
484
const previousStep = () => {
485
if (currentStep.value > 1) {
486
const prev = currentStep.value - 1;
487
router.push({
488
name: "form-step",
489
params: { step: prev.toString() }
490
});
491
}
492
};
493
```
494
495
## Types
496
497
```typescript { .api }
498
type RouteLocationRaw = string | RouteLocationPathRaw | RouteLocationNamedRaw;
499
500
interface RouteLocationPathRaw {
501
path: string;
502
query?: LocationQueryRaw;
503
hash?: string;
504
}
505
506
interface RouteLocationNamedRaw {
507
name: string | symbol;
508
params?: RouteParamsRaw;
509
query?: LocationQueryRaw;
510
hash?: string;
511
}
512
513
type RouteParams = Record<string, string | string[]>;
514
type RouteParamsRaw = Record<string, string | string[] | number | number[]>;
515
type LocationQuery = Record<string, string | string[]>;
516
type LocationQueryRaw = Record<string, string | string[] | number | number[] | null | undefined>;
517
518
interface RouteMeta {
519
[key: string | number | symbol]: unknown;
520
}
521
522
interface RouteRecordRaw {
523
path: string;
524
name?: string | symbol;
525
component?: Component;
526
components?: Record<string, Component>;
527
redirect?: RouteLocationRaw | ((to: RouteLocationNormalized) => RouteLocationRaw);
528
alias?: string | string[];
529
children?: RouteRecordRaw[];
530
meta?: RouteMeta;
531
props?: boolean | Record<string, any> | ((to: RouteLocationNormalized) => Record<string, any>);
532
beforeEnter?: NavigationGuardWithThis<undefined> | NavigationGuardWithThis<undefined>[];
533
sensitive?: boolean;
534
strict?: boolean;
535
}
536
537
interface NavigationFailure extends Error {
538
type: NavigationFailureType;
539
from: RouteLocationNormalized;
540
to: RouteLocationNormalized;
541
}
542
543
enum NavigationFailureType {
544
aborted = 4,
545
cancelled = 8,
546
duplicated = 16
547
}
548
549
type NavigationGuardNext = (to?: RouteLocationRaw | false | ((vm: ComponentPublicInstance) => any) | void) => void;
550
551
type NavigationGuardNextCallback = (vm: ComponentPublicInstance) => any;
552
553
type NavigationGuardWithThis<T> = (
554
this: T,
555
to: RouteLocationNormalized,
556
from: RouteLocationNormalized,
557
next: NavigationGuardNext
558
) => NavigationGuardReturn | Promise<NavigationGuardReturn>;
559
560
type NavigationHookAfter = (
561
to: RouteLocationNormalized,
562
from: RouteLocationNormalized,
563
failure?: NavigationFailure | void
564
) => any;
565
```
566
567
### Accessibility Features
568
569
Accessibility utilities for route announcements and screen reader support.
570
571
```typescript { .api }
572
/**
573
* Set up route announcements for screen readers
574
* @param opts - Configuration options for route announcer
575
* @returns Route announcer utilities
576
*/
577
function useRouteAnnouncer(opts?: RouteAnnouncerOptions): RouteAnnouncer;
578
579
interface RouteAnnouncerOptions {
580
/** ARIA politeness level for announcements */
581
politeness?: 'polite' | 'assertive';
582
/** Skip announcement for these routes */
583
skipRoutes?: string[];
584
/** Custom message formatter */
585
formatMessage?: (route: RouteLocationNormalized) => string;
586
}
587
588
interface RouteAnnouncer {
589
/** Announce current route */
590
announce(): void;
591
/** Skip next announcement */
592
skip(): void;
593
/** Set custom announcement message */
594
set(message: string): void;
595
}
596
```
597
598
**Usage Example:**
599
600
```typescript
601
<script setup>
602
import { useRouteAnnouncer } from "nuxt/app";
603
604
const announcer = useRouteAnnouncer({
605
politeness: 'polite',
606
skipRoutes: ['/api'],
607
formatMessage: (route) => `Navigated to ${route.meta.title || route.path}`
608
});
609
610
// Manually announce
611
announcer.announce();
612
613
// Skip next automatic announcement
614
announcer.skip();
615
616
// Custom announcement
617
announcer.set('Welcome to the dashboard');
618
</script>
619
```
620
621
## Additional Types
622
623
```typescript { .api }
624
interface RouteAnnouncerOptions {
625
/** ARIA politeness level for announcements */
626
politeness?: 'polite' | 'assertive';
627
/** Skip announcement for these routes */
628
skipRoutes?: string[];
629
/** Custom message formatter */
630
formatMessage?: (route: RouteLocationNormalized) => string;
631
}
632
633
interface RouteAnnouncer {
634
/** Announce current route */
635
announce(): void;
636
/** Skip next announcement */
637
skip(): void;
638
/** Set custom announcement message */
639
set(message: string): void;
640
}
641
```