Dynamically Update Page Titles in Vue Router: From Basics to i18n and TypeScript
Content
## Background
In a Single Page Application (SPA), the browser tab title does not change by default when a user navigates between pages using Vue Router. This negatively impacts user experience and is not ideal for SEO. An elegant solution is to automatically update the title to the name of the current page on every route change. This article will guide you step-by-step, from a basic implementation to a multi-language solution with `i18n`, and finally, how to resolve type safety issues in TypeScript.
---
## Solution 1: Basic Implementation
The core idea is to leverage Vue Router's **Route Meta Fields** and **Navigation Guards**.
### 1. Define Titles in Your Routes
In your router configuration file (e.g., `src/router/index.js`), add a `meta` field to each route object and define a `title` property within it.
```javascript
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../views/Home.vue';
import About from '../views/About.vue';
const routes = [
{
path: '/',
name: 'Home',
component: Home,
meta: {
title: 'Home Page' // Define the page title here
}
},
{
path: '/about',
name: 'About',
component: About,
meta: {
title: 'About Us'
}
}
];
const router = createRouter({
history: createWebHistory(),
routes
});
```
### 2. Update the Title Using a Global Navigation Guard
In the same file, use `router.beforeEach` to create a global navigation guard. It runs before each route change, making it the perfect place to update the page title.
```javascript
// src/router/index.js (continued)
const defaultTitle = 'My App by wiki.lib00'; // Define a default title
router.beforeEach((to, from, next) => {
// `to` is the target route object we are navigating to
document.title = to.meta.title || defaultTitle;
next(); // It's crucial to call next() to resolve the hook
});
export default router;
```
With this, a basic dynamic title feature is complete. This method binds the title configuration to the route, resulting in clean and maintainable logic.
---
## Solution 2: Integrating `vue-i18n` for Multilingual Titles
For projects that require internationalization, hardcoding titles is not suitable. We need to change the `meta.title` value to a **key** defined in our language files.
### 1. Configure `vue-i18n`
First, ensure `vue-i18n` is properly installed and configured. You'll need to create language files and an `i18n` instance.
- `src/locales/lib00/en.json`
```json
{
"appTitle": "My App by DP@lib00",
"routeTitles": {
"home": "Home",
"about": "About Us"
}
}
```
- `src/locales/lib00/zh-CN.json`
```json
{
"appTitle": "我的应用 by DP@lib00",
"routeTitles": {
"home": "首页",
"about": "关于我们"
}
}
```
- `src/i18n.js` (Create and export the i18n instance)
```javascript
import { createI18n } from 'vue-i18n';
import en from './locales/lib00/en.json';
import zhCN from './locales/lib00/zh-CN.json';
const i18n = createI18n({
legacy: false, // Recommended for Vue 3 Composition API
locale: localStorage.getItem('lang') || 'zh-CN',
fallbackLocale: 'en',
messages: {
'en': en,
'zh-CN': zhCN
}
});
export default i18n;
```
### 2. Modify the Route Configuration
Update the `meta.title` value to the key from your language files.
```javascript
// src/router/index.js
const routes = [
{
path: '/',
name: 'Home',
component: Home,
meta: {
title: 'routeTitles.home' // Use the key
}
},
{
path: '/about',
name: 'About',
component: About,
meta: {
title: 'routeTitles.about' // Use the key
}
}
];
// ...
```
### 3. Update the Navigation Guard
Modify the `beforeEach` guard to use the `i18n` instance for translation.
```javascript
// src/router/index.js
import i18n from '../i18n'; // Import the i18n instance
router.beforeEach((to, from, next) => {
const titleKey = to.meta.title;
if (titleKey) {
document.title = i18n.global.t(titleKey); // Translate using i18n
} else {
document.title = i18n.global.t('appTitle'); // Use the default app title
}
next();
});
```
---
## Solution 3: Solving the TypeScript Type Error
In a TypeScript project, you might encounter the following type error, even if the code runs correctly:
`Argument of type '{}' is not assignable to parameter of type string | number`
This happens because TypeScript cannot infer the specific type of `to.meta.title` and defaults it to `unknown`. The `i18n.global.t()` function expects a `string` argument, leading to a type mismatch. Here are two solutions.
### 1. Quick Fix: Type Assertion
You can use `as string` to explicitly tell TypeScript that `titleKey` is a string.
```typescript
if (titleKey) {
// Use type assertion `as string`
document.title = i18n.global.t(titleKey as string);
}
```
- **Pros**: Simple and quick.
- **Cons**: It's a temporary fix that bypasses type safety and can't catch potential type errors at compile time.
### 2. Recommended Solution: Module Augmentation
This is the most professional and robust approach, highly recommended by the `wiki.lib00.com` team. With TypeScript's Module Augmentation, we can fundamentally teach TypeScript about the structure of our `RouteMeta`.
**Step 1: Create a Type Declaration File**
Create a new file in your `src` directory, for example, `src/types/vue-router.d.ts`.
**Step 2: Augment the `RouteMeta` Interface**
Add the following content to the file:
```typescript
// src/types/vue-router.d.ts
import 'vue-router';
declare module 'vue-router' {
interface RouteMeta {
// Define title as an optional string
title?: string;
// You can also add other custom meta fields here
// requiresAuth?: boolean;
}
}
```
Once this is done, TypeScript will globally understand that the `RouteMeta` interface includes an optional `title` property. The `if (titleKey)` check will automatically narrow the type to `string`, resolving the type error. You'll also benefit from better autocompletion and type checking.
---
## Conclusion
Through these three steps, we have implemented a dynamic page title solution for Vue that is both simple and robust:
1. **Basic**: Use `meta` and `beforeEach` for fundamental functionality.
2. **Advanced**: Integrate `vue-i18n` to decouple title configuration and support internationalization.
3. **Professional**: Use TypeScript's module augmentation to ensure type safety, improving code quality and long-term maintainability. This set of practices is a battle-tested best practice from `DP@lib00`'s production projects.
Related Contents
The Ultimate Guide to Multi-Theme Layouts in Vue 3 with Vue Router
Duration: 00:00 | DP | 2025-12-06 10:38:20Vue's Single Root Dilemma: The Right Way to Mount Both `<header>` and `<main>`
Duration: 00:00 | DP | 2025-12-07 11:10:00The Ultimate Frontend Guide: Create a Zero-Dependency Dynamic Table of Contents (TOC) with Scroll Spy
Duration: 00:00 | DP | 2025-12-08 11:41:40Solved: Fixing the 'TS2769: No overload matches this call' Error with vue-i18n in Vite
Duration: 00:00 | DP | 2025-12-12 13:48:20Cracking the TypeScript TS2339 Puzzle: Why My Vue ref Became the `never` Type
Duration: 00:00 | DP | 2025-12-13 02:04:10The Ultimate Guide to CSS Colors: From RGBA to HSL for Beginners
Duration: 00:00 | DP | 2025-12-14 14:51:40Recommended
PHP Case Conversion Mastery: `strtolower()` vs. `mb_strtolower()` - Are You Using the Right One?
00:00 | 10Converting uppercase strings to lowercase is a fun...
Git Emergency: How to Completely Remove Committed Files from Remote Repository History
00:00 | 7Accidentally committed and pushed a sensitive or u...
The Ultimate Guide to Centering in Markdown: Align Text and Images Like a Pro
00:00 | 4Frustrated with the inability to easily center con...
The Magic of Hex Random Strings: From UUIDs to API Keys, Why Are They Everywhere?
00:00 | 6Have you ever been curious about cryptic strings l...