Vue Tip: Dynamically Change Page Title
To set the document title, we define the <title>
tag in our index.html
file:
1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <link rel="icon" href="/favicon.ico" />
6 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7 <title>Vue Tip: Dynamically Change Page Title</title>
8 </head>
9 <body>
10 <div id="app"></div>
11 <script type="module" src="/src/main.js"></script>
12 </body>
13</html>
In many applications, we have different routes and want to show a dedicated title for each route.
Unfortunately, this does not work out of the box, and we need to write some code to enable this functionality.
Let's take a look at an exemplary route configuration (check the Vue Router docs for more details about Vue Router):
1import { createRouter, createWebHistory } from 'vue-router'
2import Home from '@/views/Home.vue'
3
4const router = createRouter({
5 history: createWebHistory(),
6 routes: [
7 {
8 path: '/',
9 component: Home,
10 },
11 {
12 path: '/about',
13 component: () => import('@/views/About.vue'),
14 },
15 {
16 path: '/contact',
17 component: () => import('@/views/Contact.vue'),
18 },
19 ],
20})
21
22export default router
We can use the beforeEach guard on the router instance to dynamically set the document title on each route. The beforeEach
guard adds a navigation guard that executes before any navigation:
1router.beforeEach((to, from) => {
2 document.title = to.meta?.title ?? 'Default Title'
3})
Warning
In this example, we edit the page title using the document object, which is not advised when using server-side rendering (SSR).
I recommend the vue-meta package if you are looking for an SSR-friendly solution. It offers a more adaptable and universal approach to handling page metadata in a Vue application.
Inside the navigation guard callback, we use document.title
to set the document's title. We use the nullish coalescing operator (??
) in combination with optional chaining (?.
) to set Default Title
if the meta
object or the title
is undefined.
Finally, we need to define the metadata on our routes:
1routes: [
2 {
3 path: '/',
4 component: Home,
5 meta: {
6 title: 'Home',
7 },
8 },
9 {
10 path: '/about',
11 component: () => import('@/views/About.vue'),
12 meta: {
13 title: 'About',
14 },
15 },
16 {
17 path: '/contact',
18 component: () => import('@/views/Contact.vue'),
19 meta: {
20 title: 'Contact',
21 },
22 },
23 ],
At this point, we have updated our code to dynamically update the page title when the page changes. The following StackBlitz playground contains a running example:
We can improve the code by adding "more dynamic" page titles using route props:
1<script setup>
2const client = await useClient()
3</script>
4
5<template>
6 <router-link :to="{ name: 'ViewClient', params: { pageTitle: `${client.firstName} ${client.lastName}` } }">
7 View client details
8 </router-link>
9</template>
We can access the route params via to.params
and set the document's title accordingly:
1router.beforeEach((to, from, next) => {
2 const titleFromParams = to.params?.pageTitle
3
4 if (titleFromParams) {
5 document.title = `${titleFromParams} - ${document.title}`
6 } else {
7 document.title = to.meta?.title ?? 'Default Title'
8 }
9})
If you liked this Vue tip, follow me on X to get notified about new tips, blog posts, and more. Alternatively (or additionally), you can subscribe to my weekly Vue & Nuxt newsletter:
Vue Tip: Access DOM in Watcher Callback After Vue Updated It
To access the DOM after Vue has updated it in a watcher callback, you can use the flush post option.'
Vue Tip: Typing Component Events
To declare emits with full type inference support, we can use the defineEmits API, which is automatically available inside <script setup>.