Untold tales of Angular SEO, truly Dynamic Tags, Canonical URL, etc

SPA and SEO

Achieving SEO on SPA is tough; yes, in the year 2020 too, it is painstakingly tough. period.

But sharing the experience to save someone from going through the pain, is simple. And this post is for that.

We at Jivrus develop SPA web applications mainly using AngularJS and Angular predominantly as frontend framework for both our products and projects. It was a breeze to develop web applications in AngularJS and Angular until we hit hard reality of issues on SEO.

There are already several articles (here and there) on the Internet that teach you about SEO, SEO on SPA, SEO with AngularJS / Angular. This post is not intended to reproduce those here. Rather, provide a solid path on the decisions we made to get SEO work for us and document the undocumented solutions. Warn you, it is highly opinionated.

Wake up! AngularJS is dead. Long live AngularJS

Yes, AngularJS is dead. And it is contradictory to say 'long live' on something dead. But that is true too.

Developers coming from plan javascript and jQuery world embraced AngularJS (1.x) with 'Wow!' ness until it is evolved into Angular (2+) with completely different paradigms, syntaxes and semantics. And the new world is not in favour of AngularJS as it is not actively developed anymore (but only supported). So dead; or dying.

Nevertheless, the apps that are already developed in AngularJS and live are doing good for the purpose. As long as they are not very aspirant for new features, they are maintainable and continue to being fit for the purpose. So, long live AngularJS

Now, coming to the point of SEO, there is no native solution on AngularJS and there will not be any as it is not actively maintained. Where as Angular (2+) has a native way, called Angular Universal.

So, move to Angular (Universal) sooner than later if your apps are in AngularJS and you need SEO.

So, what is the problem with Angular Universal?

No problem! Actually expected to be fantastic when Angular Universal claimed to be the solution. And Angular is actively maintained (unlike AngularJS), where is the problem?

Yet, we faced a number of issue with SEO. A quick search on Internet revealed that we are not at all alone. There are a number of articles which covered what to do with Angular Universal. All those articles soon became like curriculum subject papers that appear to be helping the job, but not in reality.

The good, bad & worst; and the solutions

Angular Universal Itself

It is supposed to be a solution until the setup intimidates you with several trial and runs before getting settled (especially in hybrid AngularJS + Angular environment).

A lot of article covers these, so not a problem except the iterations dig your efforts.


  • Good news is, it really does a good job and improves TTFB, FP, FCP (check article if you are not aware of these)

Universal SSR hates client side references

SSR cannot use client side Javascript references that are supposed to be used only in Browsers.

  • Browser objects are strict NO
    • Window, Location, Screen, History, Popup, Navigator, Timing, Cookies
  • any plain javascript library that is not required for server
    • e.g. we needed Google API (gapi) for client side JavaScript that broke big time in SSR
    • we used several other client side javascript APIs in the project that broke SSR

Important Tip: SSR won't give you the error message on a platter for you to easily debug and fix. You need to keep eyes wide open and search on server window (not browser console logs) to catch these errors.

Solution:

Check whether it is client side - The golden rule

Manually write code to check whether code is not running in Server Side before using these browser objects or javascript libraries.

Code Snippet:

import { Inject, PLATFORM_ID } from '@angular/core';

import { isPlatformBrowser } from '@angular/common';

....

....

constructor(@Inject(PLATFORM_ID) platformId: Object) {

this.isBrowser = isPlatformBrowser(platformId);

}

And use "this.isBrowser" before doing any such client side operations in server where it is deemed as crime.

Dynamic Tags just won't work; well, until you figure it out.

Angular has Title and Meta services which makes things easier to set title and meta tags required for SEO. So, where is the difficulty?

It just works when you use it with static text (means hardcoded text in code). But just refuses to work when setting values dynamically retrieved from API/ database.

The tricky part is that the dynamic tags appear correctly set when you print in browser console and inspect the DOM. But it does not provide the tags to Search Engine crawlers (like Google Bot). Ever faced this issue and went clueless? You are joining the developers struggling out there on stackoverflow with growing number of unanswered questions.

Solution:

First make sure that Client side reference issue mentioned above is fixed.

Then, keenly watch logs to understand what is happening on server side (not browser console logs). Depending on the server you host, you will see a cryptic error message that appears. For us, on localhost, we saw the great reporting of the following error.

ERROR [error]

That's it. No error message, no stack trace and nothing to give a clue where it goes wrong.

(It could be bit different for you depending on where you run the SSR code)

Once you see this error, as a smart developer, you can figure out what is failing. Simply, keep tracing the console.log message before and after this error to narrow down on the code that is failing on server side.

For us, the httpClient.get function was failing without any reason. It worked perfectly on browser but not on server side. Digging into it, we found that Angular Universal is not able to fire API calls to relative URL. Making the URL into absolute made it work. That is, changing URL from /api/member to https://localhost:8080/api/member. Soon, we extended this to Staging and Production. And it worked like charm.

Canonical URL messes up for no reason

The popular approach for setting canonical URL in Angular is documented this article. This is just one of the articles that talks about canonical URL. It works but we noticed our sitemap URLs are not reconciling with the canonical URLs for many cases.

Solution:

Looking at the canonical URL, for some unknown reason, the canonical URL (from doc.URL) came as http:// instead of https://. We do not yet know the reason but had to handle this in code to avoid reconciliation error with sitemap. (if anyone knows the reason, please write in the comments section below)

Continuing SEO journey in Angular

We are continuing SEO journey in Angular and our aim is to get all URLs indexable by search engines.

Though we see major issues are resolved, we do expect some more issues coming up that deserves a place in this post. So you expect more getting added here.

Author: Michaes, CoFounder and Director of Jivrus Technologies.He is so passionate about building software products and solutions.

Co-author is Shashidhara, Software Developer of Jivrus Technologies. He works on best JavaScript frameworks in his everyday work to make things better for Jivrus and clients.