How to Add Dynamic Canonical Links in Angular

Search engines serve millions of users per day. Search Engine Optimization[SEO] is an indispensable part of modern web apps. One of the important SEO rules is Canonical URLs. Canonical URLs can help to inform search engines about “duplicate” or identical content appearing on multiple URLs. Adding the canonical URL in the static website is pretty straight forward. Just add a link tag like -

<link rel="canonical" href="https://yourLink">

But as we know, the content of most of the single-page apps is dynamic. Let’s take an example of a blogging app made with Angular. Suppose we have two routes, one for a list of blogs and one for the blog details page. If the user clicks on a blog from the blogs list it will open the blog details page. We need to add a dynamic canonical URL for the blog details page as per the blog content, mostly a URL formed by the title of the blog. So it’s trickier to add such dynamic canonical links in single-page apps.

For Angular, there are many ways proposed by smart developers for adding dynamic canonical links but they are somewhat complicated. There is a very simple way of adding a dynamic canonical link. We are going to create a custom directive that will append our dynamic canonical URL to the head tag. Create a new directive by using command

ng g d directives/move-to-head/move-to-head


import { Directive, Renderer2, ElementRef, Inject, OnDestroy, OnInit } from '@angular/core';
import { DOCUMENT } from '@angular/common';

  selector: '[appMoveToHead]'
export class MoveToHeadDirective implements OnDestroy, OnInit {

    private renderer?: Renderer2, 
    private elRef?: ElementRef, 
    @Inject(DOCUMENT) private document?: Document
  ) { }
  ngOnInit(): void {
    this.renderer.appendChild(this.document.head, this.elRef.nativeElement);
    this.renderer.removeAttribute(this.elRef.nativeElement, 'appmovetohead');

  ngOnDestroy(): void {
    this.renderer.removeChild(this.document.head, this.elRef.nativeElement);


It’s a simple directive that uses Renderer2 form @angular/core and appends the dom element to the head of the page.

Usage -

We have to sanitize the URL before dynamically adding it to the header. We can do this at component level but it is a good idea to write a pipe for dom sanitizing. Create a new pipe by using command

ng g p pipes/safe-url/safe-url

Add following code to it -

import { Pipe, PipeTransform} from '@angular/core';
import { DomSanitizer } from "@angular/platform-browser";

@Pipe({ name: 'safeUrl', pure: true })

export class SafeUrlPipe implements PipeTransform {

constructor(private sanitizer: DomSanitizer) { }
    transform(url) {
        return this.sanitizer.bypassSecurityTrustResourceUrl(url);

Now in blog-details-components.ts -

import { Component, OnInit } from '@angular/core';

  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
export class AppComponent implements OnInit{
  title = 'canonical';



    // Lets assume that we get the data from backend and then we form our dynamic canonical url. Here, I am hardcoding it.
    this.canonicalLink = "";

blog-details-components.html -

<div *ngIf="canonicalLink">
 <link rel="canonical" appMoveToHead [attr.href]="canonicalLink | safeUrl" />

We are removing canonical Tag from DOM when the user navigates to another page. We do this in ngOnDestroy() of MoveToHead directive. So the case of previous canonical tag being present in the new page is handled. Now check the elements in dev console. You will see your dynamic canonical link is added in the head of the page.

Time without pipe

Hope this helps while adding SEO to your angular app. Happy coding:)

See also

Note - The following commenting system is built with Angular Elements and Firebase. If you want to build your own commenting system and add it in your blog then check out the Github repo -