Search code examples
vue.jsvue-routervue-cli

Vue router: How to dynamically generate page titles for dynamic routes?


I have a simple Vue-based website created using Vue CLI, and I'm trying to figure out how to dynamically generate page titles for views w/ dynamic routes. The site is pulling JSON data from a headless CMS, and I have dynamic routes set up for detail views of a "project" content type. I have the dynamic route set up as follows:

// single project vue (dynamic)
{
    path: "/projects/:project_name_slug",
    name: "single-project",
    component: () =>
        import(
            /* webpackChunkName: "single-project" */ "./views/SingleProject.vue"
        ),
    meta: {
        title: "project detail",
    }
}

I have added the following, which works for adding static page titles:

// show page titles
const DEFAULT_TITLE = "my blog";
router.afterEach((to, from) => {
    // document.title = to.meta.title || DEFAULT_TITLE;
    document.title = "my blog - " + to.meta.title || DEFAULT_TITLE;
});

As it stands, upon visiting a project detail view, the title will read "my blog - project detail"; however, I'd like it to pull the actual project name from the JSON/field data for project_name (just as the route path is using project_name_slug), but nothing I've tried thus far has worked. For instance, using meta: { title: (route) => route.params.project_name } just results in a raw function text showing after "my blog - ". Thus, in cases for those dynamic views, I would like to.meta.title to resolve to the project name, creating "my blog - {project name}". Thank you for any assistance here, and please let me know if I need to provide any more details.

*Note: This question differs from that posed in this SO thread as my case involves dynamic routes using JSON data requested via Axios


Solution

  • Set the meta in the router's beforeEach navigation guard:

    router.beforeEach((to, from, next) => {
      to.meta.title = to.params.project_name_slug;
      next();
    });
    

    -or-

    You could use the beforeRouteEnter and beforeRouteUpdate in-component guards:

    export default {
      ...
      beforeRouteEnter(to, from, next) {
        to.meta.title = to.params.project_name_slug;
        next();
      },
      beforeRouteUpdate(to, from, next) {
        to.meta.title = to.params.project_name_slug;
        next();
      }
    }
    

    You can use the afterEach just like you would for a static meta since it will already be set:

    const DEFAULT_TITLE = "my blog";
    router.afterEach((to, from) => {
        // document.title = to.meta.title || DEFAULT_TITLE;
        document.title = "my blog - " + to.meta.title || DEFAULT_TITLE;
    });
    

    (Note: Using beforeEnter in the route definition wouldn't be enough because it doesn't trigger when going from one dynamic param to another. Even <router-view :key="$route.fullPath"></router-view> does not trigger beforeEnter when changing params.)