SEO is an integral part of any website and Canonical URLs can help to inform search engines about identical or “duplicate” content appearing on multiple URLs. A canonical tag (AKA “rel canonical”) tells search engines to use the URL defined in the canonical tag to appear in search results. You can refer this guide to find more about the importance of Canonical URL. It’s important to create the canonical URLs dynamically as a static canonical URL can also impact your search engine results. In this short post, we’ll see how to set Canonical URL in Angular 7.
How to set Canonical URL in Angular 7
If you want to learn all of Angular, I want to personally recommend ng-book as the single-best resource out there. You can get a copy here. The book is updated to Angular 7.
First, we need to create a singleton service. Why singleton? So that the service instance can be shared across multiple components. The components need to imports and reference the service in its constructor.
Create an angular service named MetaService
with the following code:
import { Injectable, Inject } from '@angular/core'; import { DOCUMENT } from '@angular/common'; @Injectable({ providedIn: 'root' }) export class MetaService { constructor(@Inject(DOCUMENT) private dom) { } createCanonicalURL() { let link: HTMLLinkElement = this.dom.createElement('link'); link.setAttribute('rel', 'canonical'); this.dom.head.appendChild(link); link.setAttribute('href', this.dom.URL); } }
The above code does the following things.
- As the service needs to be a singleton service, starting with Angular 6 this is done by setting
providedIn
to root on the service’s@Injectable
decorator [Line:4-6]. To use@Injectable
, we need to import the required modules [Line:1]. You can read more about the singleton service on New Way of Providing Shared Instance of a Service in Angular 6 - As we need to manipulate the DOM, we need something in Angular which represents DOM.
DOCUMENT
in Angular is used as browser DOM document. It is a DI token representing the main rendering context. Angular creates DOCUMENT DI token as following.const DOCUMENT: InjectionToken<Document>;
It is imported from
@angular/common
library and it is injected in the constructor using@Inject
decorator [Line:9].
Thedom
variable represents HTML DOM and we can access and manipulate DOM properties such as creating a canonical link at runtime. - The function
createCanonicalURL
uses Angulardocument
to create alink
element and set canonical URL and appends it to the HEAD section.
Use service in angular component
Now, let’s use this service in the angular component. To use this service in any angular component, 3 things needs to be done.
- Import the service.
- Inject the service in the component constructor.
- Call the
createCanonicalURL
function inngOnInit
event.
import { Component } from '@angular/core'; import { MetaService } from './meta.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'Set Canonical URL in Angular 7'; constructor(private metaService: MetaService) { } ngOnInit() { this.metaService.createCanonicalURL(); } }
Run the application and check the canonical URL via the inspector tool.
We can improve the usability of the createCanonicalURL
method by passing an optional URL parameter. This would help to pass a different canonical URL reference for the page. Like,
createCanonicalURL(url?:string) { let canURL = url == undefined ? this.dom.URL : url; let link: HTMLLinkElement = this.dom.createElement('link'); link.setAttribute('rel', 'canonical'); this.dom.head.appendChild(link); link.setAttribute('href', canURL); }
Thank you for reading. Keep visiting this blog and share this in your network. Please put your thoughts and feedback in the comments section.
The canonical link stays in the document when you navigate to another route. How would you find the old one, remove it, and add another? Or find the existing link element and replace the URL? Thank you.
Nevermind, duh! I had to remove them manually on the ngOnDestroy() event, lol. Thanks for the article thanks for sharing.
Hi, I am very new to angular and I face the same problem how are you able to remove the link on ngOnDestroy()? Did you have to add another method on your SEO service?
I’m sorry if this is a dumb question.
Just do this and you are good to go
const canonical: any = document.querySelectorAll(‘link[rel=”canonical”]’);
canonical[0].parentElement.removeChild(canonical[0]);
Add this code in your ngOnDestroy
I changed the createCanonicalURL method to delete the old one before adding the new one;
createCanonicalURL() {
let oldlink: HTMLLinkElement = this.dom.querySelector(‘link[rel=canonical]’);
if (oldlink) {
oldlink.remove();
}
let link: HTMLLinkElement = this.dom.createElement(‘link’);
link.setAttribute(‘rel’, ‘canonical’);
this.dom.head.appendChild(link);
link.setAttribute(‘href’, this.dom.URL);
}
I think a better approach would be to set an id for the canonical link element and update it in every ngOnInit, something like this:
setCanonicalURL() {
let link: HTMLLinkElement = this.dom.getElementById(‘canonicalMeta’);
link.setAttribute(‘href’, this.dom.URL);
}
A separate issue I’ve noticed is that because the tag is being appended if you route to different pages you’ll end up with multiple link tags. For me time permitting I’ll see if I can get the method to parse the head first and remove any existing canonical link tag but you might want to note this here.
I have used this in Angular 7, but it’s not showing website address, so instead of adding `www.example.com` it’s just adding `/` for the home page, some of the inner pages too.
Verify the value of this.dom.URL value. Try to log the value in the console and see.
Hi, I think you just viewed your page in view source, you should check the content or canonical url details of the page using inspect element console option.
To view it in your view source itself, you must enable angular universal for SSR.
I successfully add the canonical tag for every route, but I am not able to see the canonical tag in view-source of the page.
Based on your comments, it would be difficult to identify the error. Make sure you followed all the steps and there are no errors in the browser console window.
Use the inspect instead and you should see it.
I am facing same problem. please tell me a solution for it if you have.