In this tutorial, we learn how to add Dynamic Meta Tags in Angular. First, we will define the Meta Tags in the Angular Routes. Then use the Meta Service and NavigationEnd
router event to listen to the route change. When the route changes, we will update the Meta Tags.
Table of Contents
We use the Meta Service in Angular to add/remove meta tags. It provides the following methods
To use Meta Service, we first need to import it root module and inject it in Angular Providers as shown below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import { BrowserModule, Meta } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, AppRoutingModule ], providers: [Meta], bootstrap: [AppComponent] }) export class AppModule { } |
And in the component class, first, inject the Meta Service using Angular Dependency Injection. Use any of the methods of the Meta Service to manipulate the Tags.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import { Component, OnInit } from '@angular/core'; import { Meta, MetaDefinition } from '@angular/platform-browser'; @Component({ template: `<h1>First Component</h1>` }) export class FirstComponent { constructor(private metaService: Meta) { this.addTag(); } addTag() { this.metaService.addTag({ name: 'description', content: 'Article Description' }); this.metaService.addTag({ name: 'robots', content: 'index,follow' }); this.metaService.addTag({ property: 'og:title', content: 'Content Title for social media' }); } } |
Adding Meta tags in component class is little inconvenient. The better way is to define all the meta tags at a central place preferably when we define the routes. Whenever the route changes, we can read the Tags from the router and update Mega tags.
Example App
Create a new Angular Application. Add HomeComponent, FirstComponent, SecondComponent, SecondAComponent & ThirdComponent as shown below.
1 2 3 4 5 6 7 8 9 10 | import { Component, OnInit } from '@angular/core'; import { Meta, MetaDefinition } from '@angular/platform-browser'; @Component({ template: `<h1>Home Component</h1>` }) export class HomeComponent { } |
1 2 3 4 5 6 7 8 9 10 | import { Component, OnInit } from '@angular/core'; import { Meta, MetaDefinition } from '@angular/platform-browser'; @Component({ template: `<h1>First Component</h1>` }) export class FirstComponent { } |
1 2 3 4 5 6 7 8 9 10 | import { Component, OnInit } from '@angular/core'; import { Meta, MetaDefinition } from '@angular/platform-browser'; @Component({ template: `<h1>Second Component</h1>` }) export class SecondComponent { } |
1 2 3 4 5 6 7 8 9 10 | import { Component, OnInit } from '@angular/core'; import { Meta, MetaDefinition } from '@angular/platform-browser'; @Component({ template: `<h1>Second A Component</h1>` }) export class SecondAComponent { } |
1 2 3 4 5 6 7 8 9 10 | import { Component, OnInit } from '@angular/core'; import { Meta, MetaDefinition } from '@angular/platform-browser'; @Component({ template: `<h1>Third Component</h1>` }) export class ThirdComponent { } |
The next step is to add the Meta tags in the route
Open the app-routing.module.ts
and the routes as shown below. Add the Meta Tags property in route data. We can use the Route data to pass the static data or dynamic data to routed components. The title is also added to the Route Data.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { FirstComponent } from './first.component'; import { SecondComponent } from './second.component'; import { SecondAComponent } from './secondA.component'; import { ThirdComponent } from './third.component'; import { HomeComponent } from './home.component'; const routes: Routes = [ { path: '', component: HomeComponent, pathMatch: 'full', data: { title: 'Title for Home Component', descrption: 'Description of Home Component', ogTitle: 'Description of Home Component for social media', } }, { path: 'first', component: FirstComponent, data: { title: 'Title for First Component', descrption: 'Description of First Component', robots: 'noindex, nofollow', ogTitle: 'Description of First Component for social media', } }, { path: 'second', children: [ { path: '', component: SecondComponent, pathMatch: 'full', data: { title: 'Title for Second Component', descrption: 'Description of Second Component', } }, { path: 'a', component: SecondAComponent, data: { title: 'Title for Second A Component', descrption: 'Description of Second A Component', ogTitle: 'Title of Second A Component for social media', ogDescription: 'Description of Second A Component for social media', ogImage: 'ImagePathForSocialMedia' } }, ] }, { path: 'third', component: ThirdComponent, data: { title: 'Title for third Component', descrption: 'Description of third Component', ogDescription: 'Description of third Component for social media', ogUrl: '/home' } }, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { } |
Whenever a user navigates to a new route (or page), the router fires the navigation events. The Router raises the NavigationEnd
event, when it successfully completes the navigation.
We must listen to every NavigationEnd
event and the best place to listen is in our root component. i.e. app.component.ts
. The code is as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | import { Component, OnInit } from '@angular/core'; import { Title, Meta } from '@angular/platform-browser'; import { Router, NavigationEnd, ActivatedRoute } from '@angular/router'; import { filter, map } from 'rxjs/operators'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { constructor(private router: Router, private activatedRoute: ActivatedRoute, private titleService: Title, private metaService: Meta) { } ngOnInit() { this.router.events.pipe( filter(event => event instanceof NavigationEnd), ) .subscribe(() => { var rt = this.getChild(this.activatedRoute) rt.data.subscribe(data => { console.log(data); this.titleService.setTitle(data.title) if (data.descrption) { this.metaService.updateTag({ name: 'description', content: data.descrption }) } else { this.metaService.removeTag("name='description'") } if (data.robots) { this.metaService.updateTag({ name: 'robots', content: data.robots }) } else { this.metaService.updateTag({ name: 'robots', content: "follow,index" }) } if (data.ogUrl) { this.metaService.updateTag({ property: 'og:url', content: data.ogUrl }) } else { this.metaService.updateTag({ property: 'og:url', content: this.router.url }) } if (data.ogTitle) { this.metaService.updateTag({ property: 'og:title', content: data.ogTitle }) } else { this.metaService.removeTag("property='og:title'") } if (data.ogDescription) { this.metaService.updateTag({ property: 'og:description', content: data.ogDescription }) } else { this.metaService.removeTag("property='og:description'") } if (data.ogImage) { this.metaService.updateTag({ property: 'og:image', content: data.ogImage }) } else { this.metaService.removeTag("property='og:image'") } }) }) } getChild(activatedRoute: ActivatedRoute) { if (activatedRoute.firstChild) { return this.getChild(activatedRoute.firstChild); } else { return activatedRoute; } } } |
The following code subscribes to the router change event. The filter
RxJS Operator filters out NavigationEnd
event. We subscribe to it.
1 2 3 4 5 6 | this.router.events.pipe( filter(event => event instanceof NavigationEnd), ) .subscribe(() => { |
In the next line, we recursively traverse through the ActivatedRoute
tree to get to the current route.
1 2 3 | var rt = this.getChild(this.activatedRoute) |
Once we have the ActivateRoute of the currently loaded component, we subscribe to the data property to read the Route Data.
1 2 3 4 | rt.data.subscribe(data => { console.log(data); |
Now, we can set the title using title service
1 2 3 | this.titleService.setTitle(data.title) |
Update the Meta Description if one is defined on the route. Else we can remove the description set for the previous component.
1 2 3 4 5 6 7 | if (data.descrption) { this.metaService.updateTag({ name: 'description', content: data.descrption }) } else { this.metaService.removeTag("name='description'") } |
In cases like robots tag, it is better to set it to follow,index
as a default, if not tags were not defined.
1 2 3 4 5 6 7 | if (data.robots) { this.metaService.updateTag({ name: 'robots', content: data.robots }) } else { this.metaService.updateTag({ name: 'robots', content: "follow,index" }) } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <h1>Angular Dynamic Meta Tags Example</h1> <ul> <li><a [routerLink]="['/']">Home</a> </li> <li><a [routerLink]="['/first']">First</a> </li> <li><a [routerLink]="['/second']">Second</a> </li> <ul> <li><a [routerLink]="['/second/a']">Second A</a> </li> </ul> <li><a [routerLink]="['/third']">third</a> </li> </ul> <router-outlet></router-outlet> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import { BrowserModule, Meta } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { FirstComponent } from './first.component'; import { SecondAComponent } from './secondA.component'; import { ThirdComponent } from './third.component'; import { SecondComponent } from './second.component'; import { HomeComponent } from './home.component'; @NgModule({ declarations: [ AppComponent, FirstComponent,SecondAComponent, ThirdComponent, SecondComponent, HomeComponent ], imports: [ BrowserModule, AppRoutingModule ], providers: [Meta], bootstrap: [AppComponent] }) export class AppModule { } |
Thanks for sharing the dynamic meta tag code, it is very helpful for me
p