Angular: getting the SEO results you want with prerender.io posted on Aug 1, 2017

Pre-rendering or server side rendering is essential if you want your Angular site to be indexable by search crawlers. Angular has a server side rendering solution in Angular Universal, however it is still currently a work in progress and as such you might find it easier to use a more stable pre-rendering solution such as Prerender.io. It is a SaaS offering which is very easy to get up and running. Once you are registered you can use their middleware to intercept requests from search crawlers and return a static version of the page instead of the Angular app. This post will go through the steps you should take to make your site look it’s best before pre-rendering with the following tips (for how to set up the actual middleware see their documentation it’s really good).

Set the title

Don’t just use a single title across the whole site. Use content/component specific titles. In your component import the title service and use it to set a specific title for each route in your site.

  
  import { Title } from '@angular/platform-browser';

  ...

  export class Comp1Component implements OnInit {

    constructor(private title: Title) { }

    ngOnInit() {
      this.title.setTitle('component 1');
    }

  }
  

Set the description

UPDATE: As of version 4 Angular now has a Meta service which you should use instead of the service below.
Unfortunately the title service is limited to just the title so to set the description we will need to write out own, more flexible service called MetaService.

  
  
  import { Injectable, Inject } from '@angular/core';
  import { DOCUMENT } from '@angular/platform-browser';

  @Injectable()
  export class MetaService {

    constructor( @Inject(DOCUMENT) private document) {
    }

    getMeta(name: string): string {
      return this.document[name];
    }

    setMeta(name: string, value: string) {
      let metaTag = this.document.querySelector('meta[name="' + name + '"]');

      if (!metaTag) {
        metaTag = this.document.createElement('meta');
        metaTag.setAttribute('name', name);
        document.getElementsByTagName('HEAD')[0].appendChild(metaTag);
      }

      metaTag.setAttribute('content', value);
    }
  }
  

Having provided the service in the app.module we can then use it throughout the site in exactly the same way as Title.

Take control of when the pre-rendering occurs

For any site with dynamic content this step is essential or you will end up with partially loaded pages being rendered. In the index.html
add the script to set a property on window called prerenderReady to false.

  
  
    <script> 
      window.prerenderReady = false; 
    <\script>
  

To allow us to update the property once a component is ready we will create another service, PrerenderService.

  
  
  import { Injectable } from '@angular/core';

  declare const window: any;

  @Injectable()
  export class PrerenderService {

    constructor() { }

    readytoRender(delay?: number): void {
        const waitFor: number = delay || 0;
        setTimeout(function () {
            window.prerenderReady = true;
            console.log('prerender ready');
        }, waitFor);
    }
  }
  

When readyToRender is called it will set the prerenderReady property to true and prerender.io will create a static version of the page. This function also accepts and optional delay parameter. This is is in case there are any other factors such as animations which we want to wait for as well.

Don’t return local error and 404 pages

When you are using client side routing you normally have at least one or more error pages for when things go wrong. For example when a page gets an unknown query parameter the site might redirect to an not found page. Also you may have pages only available to authenticated users which should not be indexed at all. In these cases we can use the meta service again to set a meta data property prerender-status-code with the value of the response code prerender.io should return instead of the page.

  
  
  import { Component, OnInit } from '@angular/core';
  import { MetaService } from '../meta.service';

  @Component({
    selector: 'app-not-found',
    templateUrl: './not-found.component.html',
    styleUrls: ['./not-found.component.css']
  })
  export class NotFoundComponent implements OnInit {

    constructor(private metaService: MetaService) { }

    ngOnInit() {
      this.metaService.setMeta('prerender-status-code', '404');
    }

  }
  

Finally

I’ve put all of the above in a code sample which you can find here.