Dynamically Update Page Titles in Vue Router: From Basics to i18n and TypeScript

Published: 2025-11-20
Author: DP
Views: 10
Category: Vue
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.