Today I bring you an elegant search engine optimization solution for Angular progressive web apps.  https://angularfirebase.com/lessons/seo-angular-part-1-rendertron-meta-tags/

Live demo: https://instafire-app.firebaseapp.com

1. This solution is fully compatible with AngularFire2 
2. Will render links in twitter, facebook, etc.
3.  Is crawlable by googlebot, bingbot, etc. 
4. Does not require Angular Universal or any weird hacks

Rendertron: https://github.com/GoogleChrome/rendertron
Cloud Functions: https://firebase.google.com/docs/functions/
Inspiration: https://medium.com/dev-channel/solving-seo-with-headless-chrome-for-your-client-side-framework-288e66fdd2b7
2024-02-04T07:56:36.801Z
nova-2
2-general-nova
2024-01-09.29447
30089e05-99d1-4376-b32e-c263170674af
86ebdfdf-902f-46db-9759-1225d40a04a7
e65f6182f5311de1533a1563d9c16a771b028d63a99e655cbaf08c702bb2c603
67875a7f-c9c4-48a0-aa55-5bdb8a91c34a
deprecated
Today, I'm going to show you something that's never been done before.
I'm going to take an Angular 5 progressive web app, make it 100% optimized for search engine crawlers like Googlebot and BingBot, make all of your routes readable by social media link bots, make it fully compatible with AngularFire 2 or any other asynchronous data source, and we're going to do this all without ever using Angular Universal.
We're going to do this with a brand new library from the Google Chrome team called Rendertron.
If you're not familiar with headless Chrome or Rendertron, I recommend watching Sam Lee's talk at Polymer Summit 2017.
It is truly mind blowing if you've ever struggled with client side JavaScript frameworks and search engine optimization.
What I'm going to show you today is how to use this tool with Angular 5 and Firebase Cloud Functions, and you're going to be very surprised at how easy this is to implement with an existing Angular app.
I have a full working demo deployed to Firebase Hosting that you can test and validate, and I'm also going to be sharing all of the source code on angularfirebase.com.
If you're new here, make sure to subscribe.
And if you wanna keep this content free and frequent, consider becoming a pro supporter.
Let's take a closer look at how this system is going to work.
We have just a regular Angular Progressive Web App deployed to Firebase Hosting.
Then we have our Rendertron app on the right.
Rendertron is just a Chrome browser that can run on the server, Meaning, it can render your site just like a normal user would, and then it can return that data back to a bot that wants to read the meta tags or any asynchronous data that was loaded into the page.
From there, we're going to set up a Cloud function to serve as middleware that will determine if a user is a bot or an actual human that should receive the normal Angular app.
Let's say someone shares a link and the Facebook bot wants to crawl your site to show a preview of that link.
The Cloud function will route the request to Rendertron first, then respond to the Facebook bot with a fully rendered page.
This means you can create meta tags dynamically or load data asynchronously and it can still be read by any kind of bot.
If a regular human comes to visit the site, it's going to skip Rendertron and just serve the Angular app directly.
If this user decides to share a link from your app in social media, Yeah.
It's going to show any meta tags associated with that page.
The Chrome team has an instance of Rendertron deployed, which you can use now for your own app.
However, it's not recommended for production use.
I'm going to show you how to clone the project and deploy it to App Engine if you do wanna use this in production.
It's a Dockerized project, so you can actually deploy it to any back end that you want.
But for testing, you can just use the instance that Google has deployed.
It exposes a very simple API.
You simply pass it the URL that you wanna read, and it will render and serialize that URL.
It listens to the initial page load as well as any asynchronous events and network calls to load additional data into the page.
It gives you up to 10 seconds to render the page, but it will send the response as soon as the page is finished rendering.
So now that we know how Rendertron works, go ahead and build an Angular app that can actually put this to use.
The first thing I'm going to do is generate a new app and make sure to include the routing module.
Then I'm going to go into the index HTML file and add some meta tags for Twitter and Open Graph.
If you're not familiar with meta tags, They just tell link bots how to display your page.
To show a Twitter card, we just give it a title, description, and image.
If someone links this page, Twitter will know exactly which image to display and the description to show in the Twitter platform itself.
The meta tags I'm showing you here are designed to be a default fallback if we can't dynamically generate them.
They should convey general information about the home page of your app.
Now we want these meta tags to change for each individual component that's loaded by the router.
So we're gonna do that by generating an SEO service.
The first thing I'm doing is importing that meta service from Angular which just is able to set meta tags dynamically in the head of the document, and then we'll inject that in the constructor.
And I'm setting up a configuration object here.
You're probably gonna wanna customize this for your own app, but it just takes named arguments that you can override with your own configuration object.
The whole point of this method is to avoid repeating these meta tags over and over again for each individual component.
We can set them from the service by calling meta update tag along with the name and the content that we want to show.
The structure of these tags will mirror the meta tags we set in index g m l, and then update them when a new component is rendered.
We can set the values using that configuration object, which keeps our code dry so we're not repeating update tag tagging each individual component.
I'm going through this quickly because each update tag method is exactly the same.
Now let's put this service to use in a component.
So I'm generating a new component called About Page.
I also created a few other components and will load all of these in the router.
We will first import each component, then we'll add them to the routes array.
Each route will have a path that's where you navigate to in the browser and then the component that you'll render imperatively using that route.
Now we need to set up a few links so users can actually navigate to these routes.
We can do that using the app component and you should see the router outlet at the bottom And then we'll just do a router link for each of our paths.
Now for each of these components, we can generate the meta tags dynamically.
So all we have to do is go into the component TypeScript and import the SEO service and then during NG OnInit, we'll call that Generate tags method with the configuration options that we want to set.
So for the home page, we'll set the title and then we'll say the description is my SEO friendly home page in Angular 5 and give it an image as well.
Then we can switch over to the about page and do the exact same thing.
So that'll set the meta tags dynamically for any component that's loaded by the router.
You can verify this is working by opening your app and then going into the head of the document, And you should see that meta tags change like this each time you navigate to a new route.
That's pretty cool, but I want to take this a step further and generate the meta tags dynamically with data loaded from the Firebase database.
I have another component generated called Firebase demo that's exactly the same as all the others.
But in this one, we're going to import the Firebase database.
For this part, you need a Firebase account as well as AngularFire 2 installed.
You can follow the official instructions on the main repo.
I'm going to import the Firebase database and you could also use Firestore here as well if you wanted to.
Then we'll need an RxJS observable as well as the take operator.
First, I'm setting up a reference to the AngularFire object.
Then we'll retrieve that data as an observable.
Then we'll inject our SEO service as well as the AngularFire database in the constructor.
To make the reference, we just call DBObject and I have this data saved on the demo node, and then we get the data by calling value changes.
Let's quickly jump into Firebase and see what this data looks like.
For this demo, it looks exactly like the data we had hard coded in the components earlier, but we're gonna load it over the network via Firebase.
To set the Meta Tags, we're going to call take 1 on the observable And then subscribe to it and with the emitted value, we're going to use our SEO service to set the meta tags.
The data we get back from the subscription will be that data we just looked at in Firebase, so we can set the title description and image meta tags.
So that's all we need from Angular.
Now it's up to Rendertron and Firebase Cloud Functions to make this SEO friendly.
First, I'm going to show you how to deploy your own instance of Rendertron so you can use this in production.
Google may offer this as a service in the future and I hope that they do, but for now we have to deploy our own instance of Rendertron.
For testing, this part is completely optional.
So if you wanna skip this step, you can go straight to the Firebase Cloud function.
For this demo, I'm just going to clone Rendertron directly into my Angular project, but you'd probably wanna have it as a standalone project separate from Angular.
I'd also like to point out that you need to have Docker installed.
The system requirements vary, so I'm not able to show you that step by step here.
The next step is to CD into that directory, then run the Docker build command.
It will take a few minutes to build the container, but once it's finished you should be able to run Docker run and serve it locally on your system.
If you navigate to port 8080, you should see the Rendertron app running there.
This is great if you want to customize Rendertron source code for your specific needs.
If you already have a project with Firebase, you can deploy the container to App Engine with a single command.
You'll first wanna go into the app YAML file and set the number of CPU cores and memory that you wanna used.
If you have the Google Cloud Platform command line tools installed, you can simply call gcloudapp deploy with your corresponding Firebase project.
That's going to take another few minutes, but it'll give you back a URL with your own custom production ready version of Rendertron.
Now we're ready to create the Cloud Function middleware that will determine if the user is a bot or an actual human.
You'll need both Firebase Hosting and Firebase Functions initialized in your project.
Then we need to configure hosting to use the Firebase Cloud Function for any URL other than the root URL.
So So we do that by adding it to the rewrite section and the function is going to be called app.
But before we get into the function, I'd like to point out that Rendertron has an existing Firebase Cloud function designed to work with Polymer.
It's not compatible with Angular, but it might be worth checking out.
So getting back to our app, we're going to CD in the functions directory.
Then we're going to install Express JS, which will give us some syntactic sugar for routing and also a library called Node Fetch.
Then we're gonna go into the index JS file and import these required libraries.
We're also going to import URL from Node and then set our express app as a variable.
Then I'm going to set our URL endpoints directly in the function itself, but you could also set these as environment variables.
So the app URL is going to be the URL that we deploy our Angular app to in Firebase Hosting And then the render URL is the Rendertron endpoint.
So you can use this Rendertron endpoint exactly as it appears here.
But if you deploy your own, then you would use your own render URL.
So we'll export a function called app and that's going to give us access to a request and response object, and we're going to use the wildcard character to catch all routes.
The first thing I'm going to do is write a couple of helper functions to make this a little bit easier.
So first, we need to generate the URL from the request.
So it won't be in the correct format unless we use the Node URL library.
So we'll get the protocol, the main app URL, Then path name is the actual route the user is trying to get to.
To detect search engine crawlers and social media bots, we're going to create a big list of known bot user agents.
So probably the most famous is Googlebot, but Googlebot's good at executing JavaScript so you may not even want to include it here.
Then we'll also set up our social media bots that would be like Twitter bot, Facebook, LinkedIn, Slack, and many others.
This list isn't comprehensive.
You can definitely add additional values here to detect other bots.
The way we actually detect a bot is from its user agent header in the request.
So we're going to take that header and convert it to lowercase.
And then we're going to loop over this list of Bots.
If we detect the substring in the header, then we're going to say the bot was detected.
We can console log it for debugging and then we'll return true from the function.
Otherwise, we're just going to return false.
When the Cloud function is invoked, the very first thing we're going to do is run this DetectBot helper with the request header.
If it returns true, we'll know that we need to send this request to Rendertron first.
So if that's the case, then we're going to format the URL properly to send it to our Rendertron Tron Endpoint.
We can do that with the generate URL function we created earlier.
And then we'll call fetch to fetch the actual URL.
So we call the render URL slash bot URL.
That returns a Promise, then we'll get the text from that Promise.
That will give us a response body that we can send back to the actual crawler bot, but we're going to define some caching on it.
So we can set the cache control header, then we'll set the very header which signals which part of the request was important in determining our response.
In this case, the user agent header is what determines how we build this response.
The last thing we'll do is convert the body to a string, and then we can send that as a response from the Cloud function.
That will be a fully rendered and serialized web page that can be used by the actual crawler bot.
If we don't detect a bot, then we just want to display the main Angular app.
We can do that by simply fetching the root URL that's deployed to Firebase.
We can handle the response in the same exact way.
We'll just turn the body into a string and then send that as a response from the Cloud function.
You could probably optimize this to be faster, but it's only going to fire when you don't go to the root URL.
Because of Firebase Hosting Priorities, It will use the index HTML file when you navigate to the root URL on your project.
I discussed this in more detail in the main article, But for now, we're just going to go ahead and deploy the function.
So run Firebase deploy only functions.
Then you'll also need to create a production build for your Angular app.
So run ng build production, then run firebase deploy only hosting.
Just like magic, your progressive web app is now optimized for search engines and can be used by link bots, and you didn't have to break your Angular app in the process.
All of this is very cutting edge, so I recommend you check out my working demo and reach out on Slack if you have any questions.
For each page in the demo, there's a URL, and if you paste that into any social media outlet, you should get a link preview.
Or, if you fetch it as a bot, you should see that fully rendered HTML.
That's it for Angular SEO with Rendertron.
If this video helped you, please like and subscribe.
And, if you want advanced Exclusive content related to these topics, consider becoming a pro subscriber at angularfirebase.com.
You'll get a free copy of my book as well as 1 on 1 project consulting.
Thanks for watching and I'll see you soon.

Speaker 0: Today, I'm going to show you something that's never been done before. I'm going to take an Angular 5 progressive web app, make it 100% optimized for search engine crawlers like Googlebot and BingBot, make all of your routes readable by social media link bots, make it fully compatible with AngularFire 2 or any other asynchronous data source, and we're going to do this all without ever using Angular Universal. We're going to do this with a brand new library from the Google Chrome team called Rendertron. If you're not familiar with headless Chrome or Rendertron, I recommend watching Sam Lee's talk at Polymer Summit 2017. It is truly mind blowing if you've ever struggled with client side JavaScript frameworks and search engine optimization.

What I'm going to show you today is how to use this tool with Angular 5 and Firebase Cloud Functions, and you're going to be very surprised at how easy this is to implement with an existing Angular app. I have a full working demo deployed to Firebase Hosting that you can test and validate, and I'm also going to be sharing all of the source code on angularfirebase.com. If you're new here, make sure to subscribe. And if you wanna keep this content free and frequent, consider becoming a pro supporter. Let's take a closer look at how this system is going to work.

We have just a regular Angular Progressive Web App deployed to Firebase Hosting. Then we have our Rendertron app on the right. Rendertron is just a Chrome browser that can run on the server, Meaning, it can render your site just like a normal user would, and then it can return that data back to a bot that wants to read the meta tags or any asynchronous data that was loaded into the page. From there, we're going to set up a Cloud function to serve as middleware that will determine if a user is a bot or an actual human that should receive the normal Angular app. Let's say someone shares a link and the Facebook bot wants to crawl your site to show a preview of that link.

The Cloud function will route the request to Rendertron first, then respond to the Facebook bot with a fully rendered page. This means you can create meta tags dynamically or load data asynchronously and it can still be read by any kind of bot. If a regular human comes to visit the site, it's going to skip Rendertron and just serve the Angular app directly. If this user decides to share a link from your app in social media, Yeah. It's going to show any meta tags associated with that page.

The Chrome team has an instance of Rendertron deployed, which you can use now for your own app. However, it's not recommended for production use. I'm going to show you how to clone the project and deploy it to App Engine if you do wanna use this in production. It's a Dockerized project, so you can actually deploy it to any back end that you want. But for testing, you can just use the instance that Google has deployed.

It exposes a very simple API. You simply pass it the URL that you wanna read, and it will render and serialize that URL. It listens to the initial page load as well as any asynchronous events and network calls to load additional data into the page. It gives you up to 10 seconds to render the page, but it will send the response as soon as the page is finished rendering. So now that we know how Rendertron works, go ahead and build an Angular app that can actually put this to use.

The first thing I'm going to do is generate a new app and make sure to include the routing module. Then I'm going to go into the index HTML file and add some meta tags for Twitter and Open Graph. If you're not familiar with meta tags, They just tell link bots how to display your page. To show a Twitter card, we just give it a title, description, and image. If someone links this page, Twitter will know exactly which image to display and the description to show in the Twitter platform itself.

The meta tags I'm showing you here are designed to be a default fallback if we can't dynamically generate them. They should convey general information about the home page of your app. Now we want these meta tags to change for each individual component that's loaded by the router. So we're gonna do that by generating an SEO service. The first thing I'm doing is importing that meta service from Angular which just is able to set meta tags dynamically in the head of the document, and then we'll inject that in the constructor.

And I'm setting up a configuration object here. You're probably gonna wanna customize this for your own app, but it just takes named arguments that you can override with your own configuration object. The whole point of this method is to avoid repeating these meta tags over and over again for each individual component. We can set them from the service by calling meta update tag along with the name and the content that we want to show. The structure of these tags will mirror the meta tags we set in index g m l, and then update them when a new component is rendered.

We can set the values using that configuration object, which keeps our code dry so we're not repeating update tag tagging each individual component. I'm going through this quickly because each update tag method is exactly the same. Now let's put this service to use in a component. So I'm generating a new component called About Page. I also created a few other components and will load all of these in the router.

We will first import each component, then we'll add them to the routes array. Each route will have a path that's where you navigate to in the browser and then the component that you'll render imperatively using that route. Now we need to set up a few links so users can actually navigate to these routes. We can do that using the app component and you should see the router outlet at the bottom And then we'll just do a router link for each of our paths. Now for each of these components, we can generate the meta tags dynamically.

So all we have to do is go into the component TypeScript and import the SEO service and then during NG OnInit, we'll call that Generate tags method with the configuration options that we want to set. So for the home page, we'll set the title and then we'll say the description is my SEO friendly home page in Angular 5 and give it an image as well. Then we can switch over to the about page and do the exact same thing. So that'll set the meta tags dynamically for any component that's loaded by the router. You can verify this is working by opening your app and then going into the head of the document, And you should see that meta tags change like this each time you navigate to a new route.

That's pretty cool, but I want to take this a step further and generate the meta tags dynamically with data loaded from the Firebase database. I have another component generated called Firebase demo that's exactly the same as all the others. But in this one, we're going to import the Firebase database. For this part, you need a Firebase account as well as AngularFire 2 installed. You can follow the official instructions on the main repo.

I'm going to import the Firebase database and you could also use Firestore here as well if you wanted to. Then we'll need an RxJS observable as well as the take operator. First, I'm setting up a reference to the AngularFire object. Then we'll retrieve that data as an observable. Then we'll inject our SEO service as well as the AngularFire database in the constructor.

To make the reference, we just call DBObject and I have this data saved on the demo node, and then we get the data by calling value changes. Let's quickly jump into Firebase and see what this data looks like. For this demo, it looks exactly like the data we had hard coded in the components earlier, but we're gonna load it over the network via Firebase. To set the Meta Tags, we're going to call take 1 on the observable And then subscribe to it and with the emitted value, we're going to use our SEO service to set the meta tags. The data we get back from the subscription will be that data we just looked at in Firebase, so we can set the title description and image meta tags.

So that's all we need from Angular. Now it's up to Rendertron and Firebase Cloud Functions to make this SEO friendly. First, I'm going to show you how to deploy your own instance of Rendertron so you can use this in production. Google may offer this as a service in the future and I hope that they do, but for now we have to deploy our own instance of Rendertron. For testing, this part is completely optional.

So if you wanna skip this step, you can go straight to the Firebase Cloud function. For this demo, I'm just going to clone Rendertron directly into my Angular project, but you'd probably wanna have it as a standalone project separate from Angular. I'd also like to point out that you need to have Docker installed. The system requirements vary, so I'm not able to show you that step by step here. The next step is to CD into that directory, then run the Docker build command.

It will take a few minutes to build the container, but once it's finished you should be able to run Docker run and serve it locally on your system. If you navigate to port 8080, you should see the Rendertron app running there. This is great if you want to customize Rendertron source code for your specific needs. If you already have a project with Firebase, you can deploy the container to App Engine with a single command. You'll first wanna go into the app YAML file and set the number of CPU cores and memory that you wanna used.

If you have the Google Cloud Platform command line tools installed, you can simply call gcloudapp deploy with your corresponding Firebase project. That's going to take another few minutes, but it'll give you back a URL with your own custom production ready version of Rendertron. Now we're ready to create the Cloud Function middleware that will determine if the user is a bot or an actual human. You'll need both Firebase Hosting and Firebase Functions initialized in your project. Then we need to configure hosting to use the Firebase Cloud Function for any URL other than the root URL.

So So we do that by adding it to the rewrite section and the function is going to be called app. But before we get into the function, I'd like to point out that Rendertron has an existing Firebase Cloud function designed to work with Polymer. It's not compatible with Angular, but it might be worth checking out. So getting back to our app, we're going to CD in the functions directory. Then we're going to install Express JS, which will give us some syntactic sugar for routing and also a library called Node Fetch.

Then we're gonna go into the index JS file and import these required libraries. We're also going to import URL from Node and then set our express app as a variable. Then I'm going to set our URL endpoints directly in the function itself, but you could also set these as environment variables. So the app URL is going to be the URL that we deploy our Angular app to in Firebase Hosting And then the render URL is the Rendertron endpoint. So you can use this Rendertron endpoint exactly as it appears here.

But if you deploy your own, then you would use your own render URL. So we'll export a function called app and that's going to give us access to a request and response object, and we're going to use the wildcard character to catch all routes. The first thing I'm going to do is write a couple of helper functions to make this a little bit easier. So first, we need to generate the URL from the request. So it won't be in the correct format unless we use the Node URL library.

So we'll get the protocol, the main app URL, Then path name is the actual route the user is trying to get to. To detect search engine crawlers and social media bots, we're going to create a big list of known bot user agents. So probably the most famous is Googlebot, but Googlebot's good at executing JavaScript so you may not even want to include it here. Then we'll also set up our social media bots that would be like Twitter bot, Facebook, LinkedIn, Slack, and many others. This list isn't comprehensive.

You can definitely add additional values here to detect other bots. The way we actually detect a bot is from its user agent header in the request. So we're going to take that header and convert it to lowercase. And then we're going to loop over this list of Bots. If we detect the substring in the header, then we're going to say the bot was detected.

We can console log it for debugging and then we'll return true from the function. Otherwise, we're just going to return false. When the Cloud function is invoked, the very first thing we're going to do is run this DetectBot helper with the request header. If it returns true, we'll know that we need to send this request to Rendertron first. So if that's the case, then we're going to format the URL properly to send it to our Rendertron Tron Endpoint.

We can do that with the generate URL function we created earlier. And then we'll call fetch to fetch the actual URL. So we call the render URL slash bot URL. That returns a Promise, then we'll get the text from that Promise. That will give us a response body that we can send back to the actual crawler bot, but we're going to define some caching on it.

So we can set the cache control header, then we'll set the very header which signals which part of the request was important in determining our response. In this case, the user agent header is what determines how we build this response. The last thing we'll do is convert the body to a string, and then we can send that as a response from the Cloud function. That will be a fully rendered and serialized web page that can be used by the actual crawler bot. If we don't detect a bot, then we just want to display the main Angular app.

We can do that by simply fetching the root URL that's deployed to Firebase. We can handle the response in the same exact way. We'll just turn the body into a string and then send that as a response from the Cloud function. You could probably optimize this to be faster, but it's only going to fire when you don't go to the root URL. Because of Firebase Hosting Priorities, It will use the index HTML file when you navigate to the root URL on your project.

I discussed this in more detail in the main article, But for now, we're just going to go ahead and deploy the function. So run Firebase deploy only functions. Then you'll also need to create a production build for your Angular app. So run ng build production, then run firebase deploy only hosting. Just like magic, your progressive web app is now optimized for search engines and can be used by link bots, and you didn't have to break your Angular app in the process.

All of this is very cutting edge, so I recommend you check out my working demo and reach out on Slack if you have any questions. For each page in the demo, there's a URL, and if you paste that into any social media outlet, you should get a link preview. Or, if you fetch it as a bot, you should see that fully rendered HTML. That's it for Angular SEO with Rendertron. If this video helped you, please like and subscribe.

And, if you want advanced Exclusive content related to these topics, consider becoming a pro subscriber at angularfirebase.com. You'll get a free copy of my book as well as 1 on 1 project consulting. Thanks for watching and I'll see you soon.
Today, I'm going to show you something that's never been done before. I'm going to take an Angular 5 progressive web app, make it 100% optimized for search engine crawlers like Googlebot and BingBot, make all of your routes readable by social media link bots, make it fully compatible with AngularFire 2 or any other asynchronous data source, and we're going to do this all without ever using Angular Universal. We're going to do this with a brand new library from the Google Chrome team called Rendertron. If you're not familiar with headless Chrome or Rendertron, I recommend watching Sam Lee's talk at Polymer Summit 2017. It is truly mind blowing if you've ever struggled with client side JavaScript frameworks and search engine optimization. What I'm going to show you today is how to use this tool with Angular 5 and Firebase Cloud Functions, and you're going to be very surprised at how easy this is to implement with an existing Angular app. I have a full working demo deployed to Firebase Hosting that you can test and validate, and I'm also going to be sharing all of the source code on angularfirebase.com. If you're new here, make sure to subscribe. And if you wanna keep this content free and frequent, consider becoming a pro supporter. Let's take a closer look at how this system is going to work. We have just a regular Angular Progressive Web App deployed to Firebase Hosting. Then we have our Rendertron app on the right. Rendertron is just a Chrome browser that can run on the server, Meaning, it can render your site just like a normal user would, and then it can return that data back to a bot that wants to read the meta tags or any asynchronous data that was loaded into the page.
From there, we're going to set up a Cloud function to serve as middleware that will determine if a user is a bot or an actual human that should receive the normal Angular app. Let's say someone shares a link and the Facebook bot wants to crawl your site to show a preview of that link.
google
The Cloud function will route the request to Rendertron first, then respond to the Facebook bot with a fully rendered page. This means you can create meta tags dynamically or load data asynchronously and it can still be read by any kind of bot. If a regular human comes to visit the site, it's going to skip Rendertron and just serve the Angular app directly. If this user decides to share a link from your app in social media, Yeah. It's going to show any meta tags associated with that page.
The Chrome team has an instance of Rendertron deployed, which you can use now for your own app. However, it's not recommended for production use. I'm going to show you how to clone the project and deploy it to App Engine if you do wanna use this in production. It's a Dockerized project, so you can actually deploy it to any back end that you want. But for testing, you can just use the instance that Google has deployed. It exposes a very simple API. You simply pass it the URL that you wanna read, and it will render and serialize that URL. It listens to the initial page load as well as any asynchronous events and network calls to load additional data into the page.
google
software
software development
It gives you up to 10 seconds to render the page, but it will send the response as soon as the page is finished rendering.
So now that we know how Rendertron works, go ahead and build an Angular app that can actually put this to use. The first thing I'm going to do is generate a new app and make sure to include the routing module. Then I'm going to go into the index HTML file and add some meta tags for Twitter and Open Graph. If you're not familiar with meta tags, They just tell link bots how to display your page. To show a Twitter card, we just give it a title, description, and image. If someone links this page, Twitter will know exactly which image to display and the description to show in the Twitter platform itself. The meta tags I'm showing you here are designed to be a default fallback if we can't dynamically generate them.
twitter
animation
They should convey general information about the home page of your app. Now we want these meta tags to change for each individual component that's loaded by the router. So we're gonna do that by generating an SEO service. The first thing I'm doing is importing that meta service from Angular which just is able to set meta tags dynamically in the head of the document, and then we'll inject that in the constructor. And I'm setting up a configuration object here. You're probably gonna wanna customize this for your own app, but it just takes named arguments that you can override with your own configuration object. The whole point of this method is to avoid repeating these meta tags over and over again for each individual component. We can set them from the service by calling meta update tag along with the name and the content that we want to show. The structure of these tags will mirror the meta tags we set in index g m l, and then update them when a new component is rendered. We can set the values using that configuration object, which keeps our code dry so we're not repeating update tag tagging each individual component. I'm going through this quickly because each update tag method is exactly the same. Now let's put this service to use in a component. So I'm generating a new component called About Page. I also created a few other components and will load all of these in the router. We will first import each component, then we'll add them to the routes array. Each route will have a path that's where you navigate to in the browser and then the component that you'll render imperatively using that route. Now we need to set up a few links so users can actually navigate to these routes. We can do that using the app component and you should see the router outlet at the bottom And then we'll just do a router link for each of our paths. Now for each of these components, we can generate the meta tags dynamically. So all we have to do is go into the component TypeScript and import the SEO service and then during NG OnInit, we'll call that Generate tags method with the configuration options that we want to set. So for the home page, we'll set the title and then we'll say the description is my SEO friendly home page in Angular 5 and give it an image as well. Then we can switch over to the about page and do the exact same thing. So that'll set the meta tags dynamically for any component that's loaded by the router. You can verify this is working by opening your app and then going into the head of the document, And you should see that meta tags change like this each time you navigate to a new route. That's pretty cool, but I want to take this a step further and generate the meta tags dynamically with data loaded from the Firebase database. I have another component generated called Firebase demo that's exactly the same as all the others. But in this one, we're going to import the Firebase database. For this part, you need a Firebase account as well as AngularFire 2 installed. You can follow the official instructions on the main repo. I'm going to import the Firebase database and you could also use Firestore here as well if you wanted to. Then we'll need an RxJS observable as well as the take operator. First, I'm setting up a reference to the AngularFire object. Then we'll retrieve that data as an observable.
Then we'll inject our SEO service as well as the AngularFire database in the constructor. To make the reference, we just call DBObject and I have this data saved on the demo node, and then we get the data by calling value changes. Let's quickly jump into Firebase and see what this data looks like.
databases
For this demo, it looks exactly like the data we had hard coded in the components earlier, but we're gonna load it over the network via Firebase. To set the Meta Tags, we're going to call take 1 on the observable And then subscribe to it and with the emitted value, we're going to use our SEO service to set the meta tags. The data we get back from the subscription will be that data we just looked at in Firebase, so we can set the title description and image meta tags.
So that's all we need from Angular. Now it's up to Rendertron and Firebase Cloud Functions to make this SEO friendly. First, I'm going to show you how to deploy your own instance of Rendertron so you can use this in production. Google may offer this as a service in the future and I hope that they do, but for now we have to deploy our own instance of Rendertron. For testing, this part is completely optional. So if you wanna skip this step, you can go straight to the Firebase Cloud function.
animation
google
For this demo, I'm just going to clone Rendertron directly into my Angular project, but you'd probably wanna have it as a standalone project separate from Angular. I'd also like to point out that you need to have Docker installed. The system requirements vary, so I'm not able to show you that step by step here. The next step is to CD into that directory, then run the Docker build command. It will take a few minutes to build the container, but once it's finished you should be able to run Docker run and serve it locally on your system. If you navigate to port 8080, you should see the Rendertron app running there. This is great if you want to customize Rendertron source code for your specific needs. If you already have a project with Firebase, you can deploy the container to App Engine with a single command. You'll first wanna go into the app YAML file and set the number of CPU cores and memory that you wanna used. If you have the Google Cloud Platform command line tools installed, you can simply call gcloudapp deploy with your corresponding Firebase project. That's going to take another few minutes, but it'll give you back a URL with your own custom production ready version of Rendertron. Now we're ready to create the Cloud Function middleware that will determine if the user is a bot or an actual human. You'll need both Firebase Hosting and Firebase Functions initialized in your project. Then we need to configure hosting to use the Firebase Cloud Function for any URL other than the root URL. So So we do that by adding it to the rewrite section and the function is going to be called app. But before we get into the function, I'd like to point out that Rendertron has an existing Firebase Cloud function designed to work with Polymer. It's not compatible with Angular, but it might be worth checking out. So getting back to our app, we're going to CD in the functions directory. Then we're going to install Express JS, which will give us some syntactic sugar for routing and also a library called Node Fetch. Then we're gonna go into the index JS file and import these required libraries. We're also going to import URL from Node and then set our express app as a variable. Then I'm going to set our URL endpoints directly in the function itself, but you could also set these as environment variables. So the app URL is going to be the URL that we deploy our Angular app to in Firebase Hosting And then the render URL is the Rendertron endpoint. So you can use this Rendertron endpoint exactly as it appears here. But if you deploy your own, then you would use your own render URL.
So we'll export a function called app and that's going to give us access to a request and response object, and we're going to use the wildcard character to catch all routes. The first thing I'm going to do is write a couple of helper functions to make this a little bit easier.
programming
So first, we need to generate the URL from the request.
So it won't be in the correct format unless we use the Node URL library. So we'll get the protocol, the main app URL, Then path name is the actual route the user is trying to get to. To detect search engine crawlers and social media bots, we're going to create a big list of known bot user agents. So probably the most famous is Googlebot, but Googlebot's good at executing JavaScript so you may not even want to include it here. Then we'll also set up our social media bots that would be like Twitter bot, Facebook, LinkedIn, Slack, and many others.
google
This list isn't comprehensive. You can definitely add additional values here to detect other bots. The way we actually detect a bot is from its user agent header in the request. So we're going to take that header and convert it to lowercase. And then we're going to loop over this list of Bots. If we detect the substring in the header, then we're going to say the bot was detected. We can console log it for debugging and then we'll return true from the function. Otherwise, we're just going to return false. When the Cloud function is invoked, the very first thing we're going to do is run this DetectBot helper with the request header. If it returns true, we'll know that we need to send this request to Rendertron first. So if that's the case, then we're going to format the URL properly to send it to our Rendertron Tron Endpoint. We can do that with the generate URL function we created earlier. And then we'll call fetch to fetch the actual URL. So we call the render URL slash bot URL. That returns a Promise, then we'll get the text from that Promise. That will give us a response body that we can send back to the actual crawler bot, but we're going to define some caching on it. So we can set the cache control header, then we'll set the very header which signals which part of the request was important in determining our response. In this case, the user agent header is what determines how we build this response. The last thing we'll do is convert the body to a string, and then we can send that as a response from the Cloud function. That will be a fully rendered and serialized web page that can be used by the actual crawler bot. If we don't detect a bot, then we just want to display the main Angular app. We can do that by simply fetching the root URL that's deployed to Firebase. We can handle the response in the same exact way. We'll just turn the body into a string and then send that as a response from the Cloud function. You could probably optimize this to be faster, but it's only going to fire when you don't go to the root URL. Because of Firebase Hosting Priorities, It will use the index HTML file when you navigate to the root URL on your project. I discussed this in more detail in the main article, But for now, we're just going to go ahead and deploy the function. So run Firebase deploy only functions. Then you'll also need to create a production build for your Angular app. So run ng build production, then run firebase deploy only hosting. Just like magic, your progressive web app is now optimized for search engines and can be used by link bots, and you didn't have to break your Angular app in the process. All of this is very cutting edge, so I recommend you check out my working demo and reach out on Slack if you have any questions.
For each page in the demo, there's a URL, and if you paste that into any social media outlet, you should get a link preview. Or, if you fetch it as a bot, you should see that fully rendered HTML. That's it for Angular SEO with Rendertron. If this video helped you, please like and subscribe. And, if you want advanced Exclusive content related to these topics, consider becoming a pro subscriber at angularfirebase.com. You'll get a free copy of my book as well as 1 on 1 project consulting. Thanks for watching and I'll see you soon.
animation
youtube
multimedia
websites
seasons
publishing
Today, I'm going to show you something that's never been done before. I'm going to take an Angular 5 progressive web app, make it 100% optimized for search engine crawlers like Googlebot and BingBot, make all of your routes readable by social media link bots, make it fully compatible with AngularFire 2 or any other asynchronous data source, and we're going to do this all without ever using Angular Universal. We're going to do this with a brand new library from the Google Chrome team called Rendertron. If you're not familiar with headless Chrome or Rendertron, I recommend watching Sam Lee's talk at Polymer Summit 2017. It is truly mind blowing if you've ever struggled with client side JavaScript frameworks and search engine optimization. What I'm going to show you today is how to use this tool with Angular 5 and Firebase Cloud Functions, and you're going to be very surprised at how easy this is to implement with an existing Angular app. I have a full working demo deployed to Firebase Hosting that you can test and validate, and I'm also going to be sharing all of the source code on angularfirebase.com. If you're new here, make sure to subscribe. And if you wanna keep this content free and frequent, consider becoming a pro supporter. Let's take a closer look at how this system is going to work. We have just a regular Angular Progressive Web App deployed to Firebase Hosting. Then we have our Rendertron app on the right. Rendertron is just a Chrome browser that can run on the server, Meaning, it can render your site just like a normal user would, and then it can return that data back to a bot that wants to read the meta tags or any asynchronous data that was loaded into the page. From there, we're going to set up a Cloud function to serve as middleware that will determine if a user is a bot or an actual human that should receive the normal Angular app. Let's say someone shares a link and the Facebook bot wants to crawl your site to show a preview of that link. The Cloud function will route the request to Rendertron first, then respond to the Facebook bot with a fully rendered page. This means you can create meta tags dynamically or load data asynchronously and it can still be read by any kind of bot. If a regular human comes to visit the site, it's going to skip Rendertron and just serve the Angular app directly. If this user decides to share a link from your app in social media, Yeah. It's going to show any meta tags associated with that page. The Chrome team has an instance of Rendertron deployed, which you can use now for your own app. However, it's not recommended for production use. I'm going to show you how to clone the project and deploy it to App Engine if you do wanna use this in production. It's a Dockerized project, so you can actually deploy it to any back end that you want. But for testing, you can just use the instance that Google has deployed. It exposes a very simple API. You simply pass it the URL that you wanna read, and it will render and serialize that URL. It listens to the initial page load as well as any asynchronous events and network calls to load additional data into the page. It gives you up to 10 seconds to render the page, but it will send the response as soon as the page is finished rendering. So now that we know how Rendertron works, go ahead and build an Angular app that can actually put this to use. The first thing I'm going to do is generate a new app and make sure to include the routing module. Then I'm going to go into the index HTML file and add some meta tags for Twitter and Open Graph. If you're not familiar with meta tags, They just tell link bots how to display your page. To show a Twitter card, we just give it a title, description, and image. If someone links this page, Twitter will know exactly which image to display and the description to show in the Twitter platform itself. The meta tags I'm showing you here are designed to be a default fallback if we can't dynamically generate them. They should convey general information about the home page of your app. Now we want these meta tags to change for each individual component that's loaded by the router. So we're gonna do that by generating an SEO service. The first thing I'm doing is importing that meta service from Angular which just is able to set meta tags dynamically in the head of the document, and then we'll inject that in the constructor. And I'm setting up a configuration object here. You're probably gonna wanna customize this for your own app, but it just takes named arguments that you can override with your own configuration object. The whole point of this method is to avoid repeating these meta tags over and over again for each individual component. We can set them from the service by calling meta update tag along with the name and the content that we want to show. The structure of these tags will mirror the meta tags we set in index g m l, and then update them when a new component is rendered. We can set the values using that configuration object, which keeps our code dry so we're not repeating update tag tagging each individual component. I'm going through this quickly because each update tag method is exactly the same. Now let's put this service to use in a component. So I'm generating a new component called About Page. I also created a few other components and will load all of these in the router. We will first import each component, then we'll add them to the routes array. Each route will have a path that's where you navigate to in the browser and then the component that you'll render imperatively using that route. Now we need to set up a few links so users can actually navigate to these routes. We can do that using the app component and you should see the router outlet at the bottom And then we'll just do a router link for each of our paths. Now for each of these components, we can generate the meta tags dynamically. So all we have to do is go into the component TypeScript and import the SEO service and then during NG OnInit, we'll call that Generate tags method with the configuration options that we want to set. So for the home page, we'll set the title and then we'll say the description is my SEO friendly home page in Angular 5 and give it an image as well. Then we can switch over to the about page and do the exact same thing. So that'll set the meta tags dynamically for any component that's loaded by the router. You can verify this is working by opening your app and then going into the head of the document, And you should see that meta tags change like this each time you navigate to a new route. That's pretty cool, but I want to take this a step further and generate the meta tags dynamically with data loaded from the Firebase database. I have another component generated called Firebase demo that's exactly the same as all the others. But in this one, we're going to import the Firebase database. For this part, you need a Firebase account as well as AngularFire 2 installed. You can follow the official instructions on the main repo. I'm going to import the Firebase database and you could also use Firestore here as well if you wanted to. Then we'll need an RxJS observable as well as the take operator. First, I'm setting up a reference to the AngularFire object. Then we'll retrieve that data as an observable. Then we'll inject our SEO service as well as the AngularFire database in the constructor. To make the reference, we just call DBObject and I have this data saved on the demo node, and then we get the data by calling value changes. Let's quickly jump into Firebase and see what this data looks like. For this demo, it looks exactly like the data we had hard coded in the components earlier, but we're gonna load it over the network via Firebase. To set the Meta Tags, we're going to call take 1 on the observable And then subscribe to it and with the emitted value, we're going to use our SEO service to set the meta tags. The data we get back from the subscription will be that data we just looked at in Firebase, so we can set the title description and image meta tags. So that's all we need from Angular. Now it's up to Rendertron and Firebase Cloud Functions to make this SEO friendly. First, I'm going to show you how to deploy your own instance of Rendertron so you can use this in production. Google may offer this as a service in the future and I hope that they do, but for now we have to deploy our own instance of Rendertron. For testing, this part is completely optional. So if you wanna skip this step, you can go straight to the Firebase Cloud function. For this demo, I'm just going to clone Rendertron directly into my Angular project, but you'd probably wanna have it as a standalone project separate from Angular. I'd also like to point out that you need to have Docker installed. The system requirements vary, so I'm not able to show you that step by step here. The next step is to CD into that directory, then run the Docker build command. It will take a few minutes to build the container, but once it's finished you should be able to run Docker run and serve it locally on your system. If you navigate to port 8080, you should see the Rendertron app running there. This is great if you want to customize Rendertron source code for your specific needs. If you already have a project with Firebase, you can deploy the container to App Engine with a single command. You'll first wanna go into the app YAML file and set the number of CPU cores and memory that you wanna used. If you have the Google Cloud Platform command line tools installed, you can simply call gcloudapp deploy with your corresponding Firebase project. That's going to take another few minutes, but it'll give you back a URL with your own custom production ready version of Rendertron. Now we're ready to create the Cloud Function middleware that will determine if the user is a bot or an actual human. You'll need both Firebase Hosting and Firebase Functions initialized in your project. Then we need to configure hosting to use the Firebase Cloud Function for any URL other than the root URL. So So we do that by adding it to the rewrite section and the function is going to be called app. But before we get into the function, I'd like to point out that Rendertron has an existing Firebase Cloud function designed to work with Polymer. It's not compatible with Angular, but it might be worth checking out. So getting back to our app, we're going to CD in the functions directory. Then we're going to install Express JS, which will give us some syntactic sugar for routing and also a library called Node Fetch. Then we're gonna go into the index JS file and import these required libraries. We're also going to import URL from Node and then set our express app as a variable. Then I'm going to set our URL endpoints directly in the function itself, but you could also set these as environment variables. So the app URL is going to be the URL that we deploy our Angular app to in Firebase Hosting And then the render URL is the Rendertron endpoint. So you can use this Rendertron endpoint exactly as it appears here. But if you deploy your own, then you would use your own render URL. So we'll export a function called app and that's going to give us access to a request and response object, and we're going to use the wildcard character to catch all routes. The first thing I'm going to do is write a couple of helper functions to make this a little bit easier. So first, we need to generate the URL from the request. So it won't be in the correct format unless we use the Node URL library. So we'll get the protocol, the main app URL, Then path name is the actual route the user is trying to get to. To detect search engine crawlers and social media bots, we're going to create a big list of known bot user agents. So probably the most famous is Googlebot, but Googlebot's good at executing JavaScript so you may not even want to include it here. Then we'll also set up our social media bots that would be like Twitter bot, Facebook, LinkedIn, Slack, and many others. This list isn't comprehensive. You can definitely add additional values here to detect other bots. The way we actually detect a bot is from its user agent header in the request. So we're going to take that header and convert it to lowercase. And then we're going to loop over this list of Bots. If we detect the substring in the header, then we're going to say the bot was detected. We can console log it for debugging and then we'll return true from the function. Otherwise, we're just going to return false. When the Cloud function is invoked, the very first thing we're going to do is run this DetectBot helper with the request header. If it returns true, we'll know that we need to send this request to Rendertron first. So if that's the case, then we're going to format the URL properly to send it to our Rendertron Tron Endpoint. We can do that with the generate URL function we created earlier. And then we'll call fetch to fetch the actual URL. So we call the render URL slash bot URL. That returns a Promise, then we'll get the text from that Promise. That will give us a response body that we can send back to the actual crawler bot, but we're going to define some caching on it. So we can set the cache control header, then we'll set the very header which signals which part of the request was important in determining our response. In this case, the user agent header is what determines how we build this response. The last thing we'll do is convert the body to a string, and then we can send that as a response from the Cloud function. That will be a fully rendered and serialized web page that can be used by the actual crawler bot. If we don't detect a bot, then we just want to display the main Angular app. We can do that by simply fetching the root URL that's deployed to Firebase. We can handle the response in the same exact way. We'll just turn the body into a string and then send that as a response from the Cloud function. You could probably optimize this to be faster, but it's only going to fire when you don't go to the root URL. Because of Firebase Hosting Priorities, It will use the index HTML file when you navigate to the root URL on your project. I discussed this in more detail in the main article, But for now, we're just going to go ahead and deploy the function. So run Firebase deploy only functions. Then you'll also need to create a production build for your Angular app. So run ng build production, then run firebase deploy only hosting. Just like magic, your progressive web app is now optimized for search engines and can be used by link bots, and you didn't have to break your Angular app in the process. All of this is very cutting edge, so I recommend you check out my working demo and reach out on Slack if you have any questions. For each page in the demo, there's a URL, and if you paste that into any social media outlet, you should get a link preview. Or, if you fetch it as a bot, you should see that fully rendered HTML. That's it for Angular SEO with Rendertron. If this video helped you, please like and subscribe. And, if you want advanced Exclusive content related to these topics, consider becoming a pro subscriber at angularfirebase.com. You'll get a free copy of my book as well as 1 on 1 project consulting. Thanks for watching and I'll see you soon.
Today,
today
I'm
i'm
going
going
to
to
show
show
you
you
something
something
that's
that's
never
never
been
been
done
done
before.
before
I'm
i'm
going
going
to
to
take
take
an
an
Angular
angular
5
5
progressive
progressive
web
web
app,
app
make
make
it
it
100%
100%
optimized
optimized
for
for
search
search
engine
engine
crawlers
crawlers
like
like
Googlebot
googlebot
and
and
BingBot,
bingbot
make
make
all
all
of
of
your
your
routes
routes
readable
readable
by
by
social
social
media
media
link
link
bots,
bots
make
make
it
it
fully
fully
compatible
compatible
with
with
AngularFire
angularfire
2
2
or
or
any
any
other
other
asynchronous
asynchronous
data
data
source,
source
and
and
we're
we're
going
going
to
to
do
do
this
this
all
all
without
without
ever
ever
using
using
Angular
angular
Universal.
universal
We're
we're
going
going
to
to
do
do
this
this
with
with
a
a
brand
brand
new
new
library
library
from
from
the
the
Google
google
Chrome
chrome
team
team
called
called
Rendertron.
rendertron
If
if
you're
you're
not
not
familiar
familiar
with
with
headless
headless
Chrome
chrome
or
or
Rendertron,
rendertron
I
i
recommend
recommend
watching
watching
Sam
sam
Lee's
lee's
talk
talk
at
at
Polymer
polymer
Summit
summit
2017.
2017
It
it
is
is
truly
truly
mind
mind
blowing
blowing
if
if
you've
you've
ever
ever
struggled
struggled
with
with
client
client
side
side
JavaScript
javascript
frameworks
frameworks
and
and
search
search
engine
engine
optimization.
optimization
What
what
I'm
i'm
going
going
to
to
show
show
you
you
today
today
is
is
how
how
to
to
use
use
this
this
tool
tool
with
with
Angular
angular
5
5
and
and
Firebase
firebase
Cloud
cloud
Functions,
functions
and
and
you're
you're
going
going
to
to
be
be
very
very
surprised
surprised
at
at
how
how
easy
easy
this
this
is
is
to
to
implement
implement
with
with
an
an
existing
existing
Angular
angular
app.
app
I
i
have
have
a
a
full
full
working
working
demo
demo
deployed
deployed
to
to
Firebase
firebase
Hosting
hosting
that
that
you
you
can
can
test
test
and
and
validate,
validate
and
and
I'm
i'm
also
also
going
going
to
to
be
be
sharing
sharing
all
all
of
of
the
the
source
source
code
code
on
on
angularfirebase.com.
angularfirebase.com
If
if
you're
you're
new
new
here,
here
make
make
sure
sure
to
to
subscribe.
subscribe
And
and
if
if
you
you
wanna
wanna
keep
keep
this
this
content
content
free
free
and
and
frequent,
frequent
consider
consider
becoming
becoming
a
a
pro
pro
supporter.
supporter
Let's
let's
take
take
a
a
closer
closer
look
look
at
at
how
how
this
this
system
system
is
is
going
going
to
to
work.
work
We
we
have
have
just
just
a
a
regular
regular
Angular
angular
Progressive
progressive
Web
web
App
app
deployed
deployed
to
to
Firebase
firebase
Hosting.
hosting
Then
then
we
we
have
have
our
our
Rendertron
rendertron
app
app
on
on
the
the
right.
right
Rendertron
rendertron
is
is
just
just
a
a
Chrome
chrome
browser
browser
that
that
can
can
run
run
on
on
the
the
server,
server
Meaning,
meaning
it
it
can
can
render
render
your
your
site
site
just
just
like
like
a
a
normal
normal
user
user
would,
would
and
and
then
then
it
it
can
can
return
return
that
that
data
data
back
back
to
to
a
a
bot
bot
that
that
wants
wants
to
to
read
read
the
the
meta
meta
tags
tags
or
or
any
any
asynchronous
asynchronous
data
data
that
that
was
was
loaded
loaded
into
into
the
the
page.
page
From
from
there,
there
we're
we're
going
going
to
to
set
set
up
up
a
a
Cloud
cloud
function
function
to
to
serve
serve
as
as
middleware
middleware
that
that
will
will
determine
determine
if
if
a
a
user
user
is
is
a
a
bot
bot
or
or
an
an
actual
actual
human
human
that
that
should
should
receive
receive
the
the
normal
normal
Angular
angular
app.
app
Let's
let's
say
say
someone
someone
shares
shares
a
a
link
link
and
and
the
the
Facebook
facebook
bot
bot
wants
wants
to
to
crawl
crawl
your
your
site
site
to
to
show
show
a
a
preview
preview
of
of
that
that
link.
link
The
the
Cloud
cloud
function
function
will
will
route
route
the
the
request
request
to
to
Rendertron
rendertron
first,
first
then
then
respond
respond
to
to
the
the
Facebook
facebook
bot
bot
with
with
a
a
fully
fully
rendered
rendered
page.
page
This
this
means
means
you
you
can
can
create
create
meta
meta
tags
tags
dynamically
dynamically
or
or
load
load
data
data
asynchronously
asynchronously
and
and
it
it
can
can
still
still
be
be
read
read
by
by
any
any
kind
kind
of
of
bot.
bot
If
if
a
a
regular
regular
human
human
comes
comes
to
to
visit
visit
the
the
site,
site
it's
it's
going
going
to
to
skip
skip
Rendertron
rendertron
and
and
just
just
serve
serve
the
the
Angular
angular
app
app
directly.
directly
If
if
this
this
user
user
decides
decides
to
to
share
share
a
a
link
link
from
from
your
your
app
app
in
in
social
social
media,
media
Yeah.
yeah
It's
it's
going
going
to
to
show
show
any
any
meta
meta
tags
tags
associated
associated
with
with
that
that
page.
page
The
the
Chrome
chrome
team
team
has
has
an
an
instance
instance
of
of
Rendertron
rendertron
deployed,
deployed
which
which
you
you
can
can
use
use
now
now
for
for
your
your
own
own
app.
app
However,
however
it's
it's
not
not
recommended
recommended
for
for
production
production
use.
use
I'm
i'm
going
going
to
to
show
show
you
you
how
how
to
to
clone
clone
the
the
project
project
and
and
deploy
deploy
it
it
to
to
App
app
Engine
engine
if
if
you
you
do
do
wanna
wanna
use
use
this
this
in
in
production.
production
It's
it's
a
a
Dockerized
dockerized
project,
project
so
so
you
you
can
can
actually
actually
deploy
deploy
it
it
to
to
any
any
back
back
end
end
that
that
you
you
want.
want
But
but
for
for
testing,
testing
you
you
can
can
just
just
use
use
the
the
instance
instance
that
that
Google
google
has
has
deployed.
deployed
It
it
exposes
exposes
a
a
very
very
simple
simple
API.
api
You
you
simply
simply
pass
pass
it
it
the
the
URL
url
that
that
you
you
wanna
wanna
read,
read
and
and
it
it
will
will
render
render
and
and
serialize
serialize
that
that
URL.
url
It
it
listens
listens
to
to
the
the
initial
initial
page
page
load
load
as
as
well
well
as
as
any
any
asynchronous
asynchronous
events
events
and
and
network
network
calls
calls
to
to
load
load
additional
additional
data
data
into
into
the
the
page.
page
It
it
gives
gives
you
you
up
up
to
to
10
10
seconds
seconds
to
to
render
render
the
the
page,
page
but
but
it
it
will
will
send
send
the
the
response
response
as
as
soon
soon
as
as
the
the
page
page
is
is
finished
finished
rendering.
rendering
So
so
now
now
that
that
we
we
know
know
how
how
Rendertron
rendertron
works,
works
go
go
ahead
ahead
and
and
build
build
an
an
Angular
angular
app
app
that
that
can
can
actually
actually
put
put
this
this
to
to
use.
use
The
the
first
first
thing
thing
I'm
i'm
going
going
to
to
do
do
is
is
generate
generate
a
a
new
new
app
app
and
and
make
make
sure
sure
to
to
include
include
the
the
routing
routing
module.
module
Then
then
I'm
i'm
going
going
to
to
go
go
into
into
the
the
index
index
HTML
html
file
file
and
and
add
add
some
some
meta
meta
tags
tags
for
for
Twitter
twitter
and
and
Open
open
Graph.
graph
If
if
you're
you're
not
not
familiar
familiar
with
with
meta
meta
tags,
tags
They
they
just
just
tell
tell
link
link
bots
bots
how
how
to
to
display
display
your
your
page.
page
To
to
show
show
a
a
Twitter
twitter
card,
card
we
we
just
just
give
give
it
it
a
a
title,
title
description,
description
and
and
image.
image
If
if
someone
someone
links
links
this
this
page,
page
Twitter
twitter
will
will
know
know
exactly
exactly
which
which
image
image
to
to
display
display
and
and
the
the
description
description
to
to
show
show
in
in
the
the
Twitter
twitter
platform
platform
itself.
itself
The
the
meta
meta
tags
tags
I'm
i'm
showing
showing
you
you
here
here
are
are
designed
designed
to
to
be
be
a
a
default
default
fallback
fallback
if
if
we
we
can't
can't
dynamically
dynamically
generate
generate
them.
them
They
they
should
should
convey
convey
general
general
information
information
about
about
the
the
home
home
page
page
of
of
your
your
app.
app
Now
now
we
we
want
want
these
these
meta
meta
tags
tags
to
to
change
change
for
for
each
each
individual
individual
component
component
that's
that's
loaded
loaded
by
by
the
the
router.
router
So
so
we're
we're
gonna
gonna
do
do
that
that
by
by
generating
generating
an
an
SEO
seo
service.
service
The
the
first
first
thing
thing
I'm
i'm
doing
doing
is
is
importing
importing
that
that
meta
meta
service
service
from
from
Angular
angular
which
which
just
just
is
is
able
able
to
to
set
set
meta
meta
tags
tags
dynamically
dynamically
in
in
the
the
head
head
of
of
the
the
document,
document
and
and
then
then
we'll
we'll
inject
inject
that
that
in
in
the
the
constructor.
constructor
And
and
I'm
i'm
setting
setting
up
up
a
a
configuration
configuration
object
object
here.
here
You're
you're
probably
probably
gonna
gonna
wanna
wanna
customize
customize
this
this
for
for
your
your
own
own
app,
app
but
but
it
it
just
just
takes
takes
named
named
arguments
arguments
that
that
you
you
can
can
override
override
with
with
your
your
own
own
configuration
configuration
object.
object
The
the
whole
whole
point
point
of
of
this
this
method
method
is
is
to
to
avoid
avoid
repeating
repeating
these
these
meta
meta
tags
tags
over
over
and
and
over
over
again
again
for
for
each
each
individual
individual
component.
component
We
we
can
can
set
set
them
them
from
from
the
the
service
service
by
by
calling
calling
meta
meta
update
update
tag
tag
along
along
with
with
the
the
name
name
and
and
the
the
content
content
that
that
we
we
want
want
to
to
show.
show
The
the
structure
structure
of
of
these
these
tags
tags
will
will
mirror
mirror
the
the
meta
meta
tags
tags
we
we
set
set
in
in
index
index
g
g
m
m
l,
l
and
and
then
then
update
update
them
them
when
when
a
a
new
new
component
component
is
is
rendered.
rendered
We
we
can
can
set
set
the
the
values
values
using
using
that
that
configuration
configuration
object,
object
which
which
keeps
keeps
our
our
code
code
dry
dry
so
so
we're
we're
not
not
repeating
repeating
update
update
tag
tag
tagging
tagging
each
each
individual
individual
component.
component
I'm
i'm
going
going
through
through
this
this
quickly
quickly
because
because
each
each
update
update
tag
tag
method
method
is
is
exactly
exactly
the
the
same.
same
Now
now
let's
let's
put
put
this
this
service
service
to
to
use
use
in
in
a
a
component.
component
So
so
I'm
i'm
generating
generating
a
a
new
new
component
component
called
called
About
about
Page.
page
I
i
also
also
created
created
a
a
few
few
other
other
components
components
and
and
will
will
load
load
all
all
of
of
these
these
in
in
the
the
router.
router
We
we
will
will
first
first
import
import
each
each
component,
component
then
then
we'll
we'll
add
add
them
them
to
to
the
the
routes
routes
array.
array
Each
each
route
route
will
will
have
have
a
a
path
path
that's
that's
where
where
you
you
navigate
navigate
to
to
in
in
the
the
browser
browser
and
and
then
then
the
the
component
component
that
that
you'll
you'll
render
render
imperatively
imperatively
using
using
that
that
route.
route
Now
now
we
we
need
need
to
to
set
set
up
up
a
a
few
few
links
links
so
so
users
users
can
can
actually
actually
navigate
navigate
to
to
these
these
routes.
routes
We
we
can
can
do
do
that
that
using
using
the
the
app
app
component
component
and
and
you
you
should
should
see
see
the
the
router
router
outlet
outlet
at
at
the
the
bottom
bottom
And
and
then
then
we'll
we'll
just
just
do
do
a
a
router
router
link
link
for
for
each
each
of
of
our
our
paths.
paths
Now
now
for
for
each
each
of
of
these
these
components,
components
we
we
can
can
generate
generate
the
the
meta
meta
tags
tags
dynamically.
dynamically
So
so
all
all
we
we
have
have
to
to
do
do
is
is
go
go
into
into
the
the
component
component
TypeScript
typescript
and
and
import
import
the
the
SEO
seo
service
service
and
and
then
then
during
during
NG
ng
OnInit,
oninit
we'll
we'll
call
call
that
that
Generate
generate
tags
tags
method
method
with
with
the
the
configuration
configuration
options
options
that
that
we
we
want
want
to
to
set.
set
So
so
for
for
the
the
home
home
page,
page
we'll
we'll
set
set
the
the
title
title
and
and
then
then
we'll
we'll
say
say
the
the
description
description
is
is
my
my
SEO
seo
friendly
friendly
home
home
page
page
in
in
Angular
angular
5
5
and
and
give
give
it
it
an
an
image
image
as
as
well.
well
Then
then
we
we
can
can
switch
switch
over
over
to
to
the
the
about
about
page
page
and
and
do
do
the
the
exact
exact
same
same
thing.
thing
So
so
that'll
that'll
set
set
the
the
meta
meta
tags
tags
dynamically
dynamically
for
for
any
any
component
component
that's
that's
loaded
loaded
by
by
the
the
router.
router
You
you
can
can
verify
verify
this
this
is
is
working
working
by
by
opening
opening
your
your
app
app
and
and
then
then
going
going
into
into
the
the
head
head
of
of
the
the
document,
document
And
and
you
you
should
should
see
see
that
that
meta
meta
tags
tags
change
change
like
like
this
this
each
each
time
time
you
you
navigate
navigate
to
to
a
a
new
new
route.
route
That's
that's
pretty
pretty
cool,
cool
but
but
I
i
want
want
to
to
take
take
this
this
a
a
step
step
further
further
and
and
generate
generate
the
the
meta
meta
tags
tags
dynamically
dynamically
with
with
data
data
loaded
loaded
from
from
the
the
Firebase
firebase
database.
database
I
i
have
have
another
another
component
component
generated
generated
called
called
Firebase
firebase
demo
demo
that's
that's
exactly
exactly
the
the
same
same
as
as
all
all
the
the
others.
others
But
but
in
in
this
this
one,
one
we're
we're
going
going
to
to
import
import
the
the
Firebase
firebase
database.
database
For
for
this
this
part,
part
you
you
need
need
a
a
Firebase
firebase
account
account
as
as
well
well
as
as
AngularFire
angularfire
2
2
installed.
installed
You
you
can
can
follow
follow
the
the
official
official
instructions
instructions
on
on
the
the
main
main
repo.
repo
I'm
i'm
going
going
to
to
import
import
the
the
Firebase
firebase
database
database
and
and
you
you
could
could
also
also
use
use
Firestore
firestore
here
here
as
as
well
well
if
if
you
you
wanted
wanted
to.
to
Then
then
we'll
we'll
need
need
an
an
RxJS
rxjs
observable
observable
as
as
well
well
as
as
the
the
take
take
operator.
operator
First,
first
I'm
i'm
setting
setting
up
up
a
a
reference
reference
to
to
the
the
AngularFire
angularfire
object.
object
Then
then
we'll
we'll
retrieve
retrieve
that
that
data
data
as
as
an
an
observable.
observable
Then
then
we'll
we'll
inject
inject
our
our
SEO
seo
service
service
as
as
well
well
as
as
the
the
AngularFire
angularfire
database
database
in
in
the
the
constructor.
constructor
To
to
make
make
the
the
reference,
reference
we
we
just
just
call
call
DBObject
dbobject
and
and
I
i
have
have
this
this
data
data
saved
saved
on
on
the
the
demo
demo
node,
node
and
and
then
then
we
we
get
get
the
the
data
data
by
by
calling
calling
value
value
changes.
changes
Let's
let's
quickly
quickly
jump
jump
into
into
Firebase
firebase
and
and
see
see
what
what
this
this
data
data
looks
looks
like.
like
For
for
this
this
demo,
demo
it
it
looks
looks
exactly
exactly
like
like
the
the
data
data
we
we
had
had
hard
hard
coded
coded
in
in
the
the
components
components
earlier,
earlier
but
but
we're
we're
gonna
gonna
load
load
it
it
over
over
the
the
network
network
via
via
Firebase.
firebase
To
to
set
set
the
the
Meta
meta
Tags,
tags
we're
we're
going
going
to
to
call
call
take
take
1
1
on
on
the
the
observable
observable
And
and
then
then
subscribe
subscribe
to
to
it
it
and
and
with
with
the
the
emitted
emitted
value,
value
we're
we're
going
going
to
to
use
use
our
our
SEO
seo
service
service
to
to
set
set
the
the
meta
meta
tags.
tags
The
the
data
data
we
we
get
get
back
back
from
from
the
the
subscription
subscription
will
will
be
be
that
that
data
data
we
we
just
just
looked
looked
at
at
in
in
Firebase,
firebase
so
so
we
we
can
can
set
set
the
the
title
title
description
description
and
and
image
image
meta
meta
tags.
tags
So
so
that's
that's
all
all
we
we
need
need
from
from
Angular.
angular
Now
now
it's
it's
up
up
to
to
Rendertron
rendertron
and
and
Firebase
firebase
Cloud
cloud
Functions
functions
to
to
make
make
this
this
SEO
seo
friendly.
friendly
First,
first
I'm
i'm
going
going
to
to
show
show
you
you
how
how
to
to
deploy
deploy
your
your
own
own
instance
instance
of
of
Rendertron
rendertron
so
so
you
you
can
can
use
use
this
this
in
in
production.
production
Google
google
may
may
offer
offer
this
this
as
as
a
a
service
service
in
in
the
the
future
future
and
and
I
i
hope
hope
that
that
they
they
do,
do
but
but
for
for
now
now
we
we
have
have
to
to
deploy
deploy
our
our
own
own
instance
instance
of
of
Rendertron.
rendertron
For
for
testing,
testing
this
this
part
part
is
is
completely
completely
optional.
optional
So
so
if
if
you
you
wanna
wanna
skip
skip
this
this
step,
step
you
you
can
can
go
go
straight
straight
to
to
the
the
Firebase
firebase
Cloud
cloud
function.
function
For
for
this
this
demo,
demo
I'm
i'm
just
just
going
going
to
to
clone
clone
Rendertron
rendertron
directly
directly
into
into
my
my
Angular
angular
project,
project
but
but
you'd
you'd
probably
probably
wanna
wanna
have
have
it
it
as
as
a
a
standalone
standalone
project
project
separate
separate
from
from
Angular.
angular
I'd
i'd
also
also
like
like
to
to
point
point
out
out
that
that
you
you
need
need
to
to
have
have
Docker
docker
installed.
installed
The
the
system
system
requirements
requirements
vary,
vary
so
so
I'm
i'm
not
not
able
able
to
to
show
show
you
you
that
that
step
step
by
by
step
step
here.
here
The
the
next
next
step
step
is
is
to
to
CD
cd
into
into
that
that
directory,
directory
then
then
run
run
the
the
Docker
docker
build
build
command.
command
It
it
will
will
take
take
a
a
few
few
minutes
minutes
to
to
build
build
the
the
container,
container
but
but
once
once
it's
it's
finished
finished
you
you
should
should
be
be
able
able
to
to
run
run
Docker
docker
run
run
and
and
serve
serve
it
it
locally
locally
on
on
your
your
system.
system
If
if
you
you
navigate
navigate
to
to
port
port
8080,
8080
you
you
should
should
see
see
the
the
Rendertron
rendertron
app
app
running
running
there.
there
This
this
is
is
great
great
if
if
you
you
want
want
to
to
customize
customize
Rendertron
rendertron
source
source
code
code
for
for
your
your
specific
specific
needs.
needs
If
if
you
you
already
already
have
have
a
a
project
project
with
with
Firebase,
firebase
you
you
can
can
deploy
deploy
the
the
container
container
to
to
App
app
Engine
engine
with
with
a
a
single
single
command.
command
You'll
you'll
first
first
wanna
wanna
go
go
into
into
the
the
app
app
YAML
yaml
file
file
and
and
set
set
the
the
number
number
of
of
CPU
cpu
cores
cores
and
and
memory
memory
that
that
you
you
wanna
wanna
used.
used
If
if
you
you
have
have
the
the
Google
google
Cloud
cloud
Platform
platform
command
command
line
line
tools
tools
installed,
installed
you
you
can
can
simply
simply
call
call
gcloudapp
gcloudapp
deploy
deploy
with
with
your
your
corresponding
corresponding
Firebase
firebase
project.
project
That's
that's
going
going
to
to
take
take
another
another
few
few
minutes,
minutes
but
but
it'll
it'll
give
give
you
you
back
back
a
a
URL
url
with
with
your
your
own
own
custom
custom
production
production
ready
ready
version
version
of
of
Rendertron.
rendertron
Now
now
we're
we're
ready
ready
to
to
create
create
the
the
Cloud
cloud
Function
function
middleware
middleware
that
that
will
will
determine
determine
if
if
the
the
user
user
is
is
a
a
bot
bot
or
or
an
an
actual
actual
human.
human
You'll
you'll
need
need
both
both
Firebase
firebase
Hosting
hosting
and
and
Firebase
firebase
Functions
functions
initialized
initialized
in
in
your
your
project.
project
Then
then
we
we
need
need
to
to
configure
configure
hosting
hosting
to
to
use
use
the
the
Firebase
firebase
Cloud
cloud
Function
function
for
for
any
any
URL
url
other
other
than
than
the
the
root
root
URL.
url
So
so
So
so
we
we
do
do
that
that
by
by
adding
adding
it
it
to
to
the
the
rewrite
rewrite
section
section
and
and
the
the
function
function
is
is
going
going
to
to
be
be
called
called
app.
app
But
but
before
before
we
we
get
get
into
into
the
the
function,
function
I'd
i'd
like
like
to
to
point
point
out
out
that
that
Rendertron
rendertron
has
has
an
an
existing
existing
Firebase
firebase
Cloud
cloud
function
function
designed
designed
to
to
work
work
with
with
Polymer.
polymer
It's
it's
not
not
compatible
compatible
with
with
Angular,
angular
but
but
it
it
might
might
be
be
worth
worth
checking
checking
out.
out
So
so
getting
getting
back
back
to
to
our
our
app,
app
we're
we're
going
going
to
to
CD
cd
in
in
the
the
functions
functions
directory.
directory
Then
then
we're
we're
going
going
to
to
install
install
Express
express
JS,
js
which
which
will
will
give
give
us
us
some
some
syntactic
syntactic
sugar
sugar
for
for
routing
routing
and
and
also
also
a
a
library
library
called
called
Node
node
Fetch.
fetch
Then
then
we're
we're
gonna
gonna
go
go
into
into
the
the
index
index
JS
js
file
file
and
and
import
import
these
these
required
required
libraries.
libraries
We're
we're
also
also
going
going
to
to
import
import
URL
url
from
from
Node
node
and
and
then
then
set
set
our
our
express
express
app
app
as
as
a
a
variable.
variable
Then
then
I'm
i'm
going
going
to
to
set
set
our
our
URL
url
endpoints
endpoints
directly
directly
in
in
the
the
function
function
itself,
itself
but
but
you
you
could
could
also
also
set
set
these
these
as
as
environment
environment
variables.
variables
So
so
the
the
app
app
URL
url
is
is
going
going
to
to
be
be
the
the
URL
url
that
that
we
we
deploy
deploy
our
our
Angular
angular
app
app
to
to
in
in
Firebase
firebase
Hosting
hosting
And
and
then
then
the
the
render
render
URL
url
is
is
the
the
Rendertron
rendertron
endpoint.
endpoint
So
so
you
you
can
can
use
use
this
this
Rendertron
rendertron
endpoint
endpoint
exactly
exactly
as
as
it
it
appears
appears
here.
here
But
but
if
if
you
you
deploy
deploy
your
your
own,
own
then
then
you
you
would
would
use
use
your
your
own
own
render
render
URL.
url
So
so
we'll
we'll
export
export
a
a
function
function
called
called
app
app
and
and
that's
that's
going
going
to
to
give
give
us
us
access
access
to
to
a
a
request
request
and
and
response
response
object,
object
and
and
we're
we're
going
going
to
to
use
use
the
the
wildcard
wildcard
character
character
to
to
catch
catch
all
all
routes.
routes
The
the
first
first
thing
thing
I'm
i'm
going
going
to
to
do
do
is
is
write
write
a
a
couple
couple
of
of
helper
helper
functions
functions
to
to
make
make
this
this
a
a
little
little
bit
bit
easier.
easier
So
so
first,
first
we
we
need
need
to
to
generate
generate
the
the
URL
url
from
from
the
the
request.
request
So
so
it
it
won't
won't
be
be
in
in
the
the
correct
correct
format
format
unless
unless
we
we
use
use
the
the
Node
node
URL
url
library.
library
So
so
we'll
we'll
get
get
the
the
protocol,
protocol
the
the
main
main
app
app
URL,
url
Then
then
path
path
name
name
is
is
the
the
actual
actual
route
route
the
the
user
user
is
is
trying
trying
to
to
get
get
to.
to
To
to
detect
detect
search
search
engine
engine
crawlers
crawlers
and
and
social
social
media
media
bots,
bots
we're
we're
going
going
to
to
create
create
a
a
big
big
list
list
of
of
known
known
bot
bot
user
user
agents.
agents
So
so
probably
probably
the
the
most
most
famous
famous
is
is
Googlebot,
googlebot
but
but
Googlebot's
googlebot's
good
good
at
at
executing
executing
JavaScript
javascript
so
so
you
you
may
may
not
not
even
even
want
want
to
to
include
include
it
it
here.
here
Then
then
we'll
we'll
also
also
set
set
up
up
our
our
social
social
media
media
bots
bots
that
that
would
would
be
be
like
like
Twitter
twitter
bot,
bot
Facebook,
facebook
LinkedIn,
linkedin
Slack,
slack
and
and
many
many
others.
others
This
this
list
list
isn't
isn't
comprehensive.
comprehensive
You
you
can
can
definitely
definitely
add
add
additional
additional
values
values
here
here
to
to
detect
detect
other
other
bots.
bots
The
the
way
way
we
we
actually
actually
detect
detect
a
a
bot
bot
is
is
from
from
its
its
user
user
agent
agent
header
header
in
in
the
the
request.
request
So
so
we're
we're
going
going
to
to
take
take
that
that
header
header
and
and
convert
convert
it
it
to
to
lowercase.
lowercase
And
and
then
then
we're
we're
going
going
to
to
loop
loop
over
over
this
this
list
list
of
of
Bots.
bots
If
if
we
we
detect
detect
the
the
substring
substring
in
in
the
the
header,
header
then
then
we're
we're
going
going
to
to
say
say
the
the
bot
bot
was
was
detected.
detected
We
we
can
can
console
console
log
log
it
it
for
for
debugging
debugging
and
and
then
then
we'll
we'll
return
return
true
true
from
from
the
the
function.
function
Otherwise,
otherwise
we're
we're
just
just
going
going
to
to
return
return
false.
false
When
when
the
the
Cloud
cloud
function
function
is
is
invoked,
invoked
the
the
very
very
first
first
thing
thing
we're
we're
going
going
to
to
do
do
is
is
run
run
this
this
DetectBot
detectbot
helper
helper
with
with
the
the
request
request
header.
header
If
if
it
it
returns
returns
true,
true
we'll
we'll
know
know
that
that
we
we
need
need
to
to
send
send
this
this
request
request
to
to
Rendertron
rendertron
first.
first
So
so
if
if
that's
that's
the
the
case,
case
then
then
we're
we're
going
going
to
to
format
format
the
the
URL
url
properly
properly
to
to
send
send
it
it
to
to
our
our
Rendertron
rendertron
Tron
tron
Endpoint.
endpoint
We
we
can
can
do
do
that
that
with
with
the
the
generate
generate
URL
url
function
function
we
we
created
created
earlier.
earlier
And
and
then
then
we'll
we'll
call
call
fetch
fetch
to
to
fetch
fetch
the
the
actual
actual
URL.
url
So
so
we
we
call
call
the
the
render
render
URL
url
slash
slash
bot
bot
URL.
url
That
that
returns
returns
a
a
Promise,
promise
then
then
we'll
we'll
get
get
the
the
text
text
from
from
that
that
Promise.
promise
That
that
will
will
give
give
us
us
a
a
response
response
body
body
that
that
we
we
can
can
send
send
back
back
to
to
the
the
actual
actual
crawler
crawler
bot,
bot
but
but
we're
we're
going
going
to
to
define
define
some
some
caching
caching
on
on
it.
it
So
so
we
we
can
can
set
set
the
the
cache
cache
control
control
header,
header
then
then
we'll
we'll
set
set
the
the
very
very
header
header
which
which
signals
signals
which
which
part
part
of
of
the
the
request
request
was
was
important
important
in
in
determining
determining
our
our
response.
response
In
in
this
this
case,
case
the
the
user
user
agent
agent
header
header
is
is
what
what
determines
determines
how
how
we
we
build
build
this
this
response.
response
The
the
last
last
thing
thing
we'll
we'll
do
do
is
is
convert
convert
the
the
body
body
to
to
a
a
string,
string
and
and
then
then
we
we
can
can
send
send
that
that
as
as
a
a
response
response
from
from
the
the
Cloud
cloud
function.
function
That
that
will
will
be
be
a
a
fully
fully
rendered
rendered
and
and
serialized
serialized
web
web
page
page
that
that
can
can
be
be
used
used
by
by
the
the
actual
actual
crawler
crawler
bot.
bot
If
if
we
we
don't
don't
detect
detect
a
a
bot,
bot
then
then
we
we
just
just
want
want
to
to
display
display
the
the
main
main
Angular
angular
app.
app
We
we
can
can
do
do
that
that
by
by
simply
simply
fetching
fetching
the
the
root
root
URL
url
that's
that's
deployed
deployed
to
to
Firebase.
firebase
We
we
can
can
handle
handle
the
the
response
response
in
in
the
the
same
same
exact
exact
way.
way
We'll
we'll
just
just
turn
turn
the
the
body
body
into
into
a
a
string
string
and
and
then
then
send
send
that
that
as
as
a
a
response
response
from
from
the
the
Cloud
cloud
function.
function
You
you
could
could
probably
probably
optimize
optimize
this
this
to
to
be
be
faster,
faster
but
but
it's
it's
only
only
going
going
to
to
fire
fire
when
when
you
you
don't
don't
go
go
to
to
the
the
root
root
URL.
url
Because
because
of
of
Firebase
firebase
Hosting
hosting
Priorities,
priorities
It
it
will
will
use
use
the
the
index
index
HTML
html
file
file
when
when
you
you
navigate
navigate
to
to
the
the
root
root
URL
url
on
on
your
your
project.
project
I
i
discussed
discussed
this
this
in
in
more
more
detail
detail
in
in
the
the
main
main
article,
article
But
but
for
for
now,
now
we're
we're
just
just
going
going
to
to
go
go
ahead
ahead
and
and
deploy
deploy
the
the
function.
function
So
so
run
run
Firebase
firebase
deploy
deploy
only
only
functions.
functions
Then
then
you'll
you'll
also
also
need
need
to
to
create
create
a
a
production
production
build
build
for
for
your
your
Angular
angular
app.
app
So
so
run
run
ng
ng
build
build
production,
production
then
then
run
run
firebase
firebase
deploy
deploy
only
only
hosting.
hosting
Just
just
like
like
magic,
magic
your
your
progressive
progressive
web
web
app
app
is
is
now
now
optimized
optimized
for
for
search
search
engines
engines
and
and
can
can
be
be
used
used
by
by
link
link
bots,
bots
and
and
you
you
didn't
didn't
have
have
to
to
break
break
your
your
Angular
angular
app
app
in
in
the
the
process.
process
All
all
of
of
this
this
is
is
very
very
cutting
cutting
edge,
edge
so
so
I
i
recommend
recommend
you
you
check
check
out
out
my
my
working
working
demo
demo
and
and
reach
reach
out
out
on
on
Slack
slack
if
if
you
you
have
have
any
any
questions.
questions
For
for
each
each
page
page
in
in
the
the
demo,
demo
there's
there's
a
a
URL,
url
and
and
if
if
you
you
paste
paste
that
that
into
into
any
any
social
social
media
media
outlet,
outlet
you
you
should
should
get
get
a
a
link
link
preview.
preview
Or,
or
if
if
you
you
fetch
fetch
it
it
as
as
a
a
bot,
bot
you
you
should
should
see
see
that
that
fully
fully
rendered
rendered
HTML.
html
That's
that's
it
it
for
for
Angular
angular
SEO
seo
with
with
Rendertron.
rendertron
If
if
this
this
video
video
helped
helped
you,
you
please
please
like
like
and
and
subscribe.
subscribe
And,
and
if
if
you
you
want
want
advanced
advanced
Exclusive
exclusive
content
content
related
related
to
to
these
these
topics,
topics
consider
consider
becoming
becoming
a
a
pro
pro
subscriber
subscriber
at
at
angularfirebase.com.
angularfirebase.com
You'll
you'll
get
get
a
a
free
free
copy
copy
of
of
my
my
book
book
as
as
well
well
as
as
1
1
on
on
1
1
project
project
consulting.
consulting
Thanks
thanks
for
for
watching
watching
and
and
I'll
i'll
see
see
you
you
soon.
soon
success
The speaker shows a demonstration of a Angular 5 web app designed to run on any browser and is specifically designed for search engines and Cloud functions. They demonstrate how to use the app in a new project and recommend a Dockerized version for production use. The speaker also explains how to generate meta tags and set up links and URL paths for each component, and how to deploy the app in Angular. They stress the importance of optimizing the Angular app for search engines and recommend using a free copy of their book and a one-time project consulting.
Angular SEO with Rendertron
Today, I'm going to show you something that's never been done before. I'm going to take an Angular 5 progressive web app, make it 100% optimized for search engine crawlers like Googlebot and BingBot, make all of your routes readable by social media link bots, make it fully compatible with AngularFire 2 or any other asynchronous data source, and we're going to do this all without ever using Angular Universal. We're going to do this with a brand new library from the Google Chrome team called Rendertron. If you're not familiar with headless Chrome or Rendertron, I recommend watching Sam Lee's talk at Polymer Summit 2017. It is truly mind blowing if you've ever struggled with client side JavaScript frameworks and search engine optimization. What I'm going to show you today is how to use this tool with Angular 5 and Firebase Cloud Functions, and you're going to be very surprised at how easy this is to implement with an existing Angular app. I have a full working demo deployed to Firebase Hosting that you can test and validate, and I'm also going to be sharing all of the source code on angularfirebase.com. If you're new here, make sure to subscribe. And if you wanna keep this content free and frequent, consider becoming a pro supporter. Let's take a closer look at how this system is going to work. We have just a regular Angular Progressive Web App deployed to Firebase Hosting. Then we have our Rendertron app on the right. Rendertron is just a Chrome browser that can run on the server, Meaning, it can render your site just like a normal user would, and then it can return that data back to a bot that wants to read the meta tags or any asynchronous data that was loaded into the page. From there, we're going to set up a Cloud function to serve as middleware that will determine if a user is a bot or an actual human that should receive the normal Angular app. Let's say someone shares a link and the Facebook bot wants to crawl your site to show a preview of that link. The Cloud function will route the request to Rendertron first, then respond to the Facebook bot with a fully rendered page. This means you can create meta tags dynamically or load data asynchronously and it can still be read by any kind of bot. If a regular human comes to visit the site, it's going to skip Rendertron and just serve the Angular app directly. If this user decides to share a link from your app in social media, Yeah. It's going to show any meta tags associated with that page. The Chrome team has an instance of Rendertron deployed, which you can use now for your own app. However, it's not recommended for production use. I'm going to show you how to clone the project and deploy it to App Engine if you do wanna use this in production. It's a Dockerized project, so you can actually deploy it to any back end that you want. But for testing, you can just use the instance that Google has deployed. It exposes a very simple API. You simply pass it the URL that you wanna read, and it will render and serialize that URL. It listens to the initial page load as well as any asynchronous events and network calls to load additional data into the page. It gives you up to 10 seconds to render the page, but it will send the response as soon as the page is finished rendering. So now that we know how Rendertron works, go ahead and build an Angular app that can actually put this to use. The first thing I'm going to do is generate a new app and make sure to include the routing module. Then I'm going to go into the index HTML file and add some meta tags for Twitter and Open Graph. If you're not familiar with meta tags, They just tell link bots how to display your page. To show a Twitter card, we just give it a title, description, and image. If someone links this page, Twitter will know exactly which image to display and the description to show in the Twitter platform itself. The meta tags I'm showing you here are designed to be a default fallback if we can't dynamically generate them. They should convey general information about the home page of your app. Now we want these meta tags to change for each individual component that's loaded by the router. So we're gonna do that by generating an SEO service. The first thing I'm doing is importing that meta service from Angular which just is able to set meta tags dynamically in the head of the document, and then we'll inject that in the constructor. And I'm setting up a configuration object here. You're probably gonna wanna customize this for your own app, but it just takes named arguments that you can override with your own configuration object. The whole point of this method is to avoid repeating these meta tags over and over again for each individual component. We can set them from the service by calling meta update tag along with the name and the content that we want to show. The structure of these tags will mirror the meta tags we set in index g m l, and then update them when a new component is rendered. We can set the values using that configuration object, which keeps our code dry so we're not repeating update tag tagging each individual component. I'm going through this quickly because each update tag method is exactly the same. Now let's put this service to use in a component. So I'm generating a new component called About Page. I also created a few other components and will load all of these in the router. We will first import each component, then we'll add them to the routes array. Each route will have a path that's where you navigate to in the browser and then the component that you'll render imperatively using that route. Now we need to set up a few links so users can actually navigate to these routes. We can do that using the app component and you should see the router outlet at the bottom And then we'll just do a router link for each of our paths. Now for each of these components, we can generate the meta tags dynamically. So all we have to do is go into the component TypeScript and import the SEO service and then during NG OnInit, we'll call that Generate tags method with the configuration options that we want to set. So for the home page, we'll set the title and then we'll say the description is my SEO friendly home page in Angular 5 and give it an image as well. Then we can switch over to the about page and do the exact same thing. So that'll set the meta tags dynamically for any component that's loaded by the router. You can verify this is working by opening your app and then going into the head of the document, And you should see that meta tags change like this each time you navigate to a new route. That's pretty cool, but I want to take this a step further and generate the meta tags dynamically with data loaded from the Firebase database. I have another component generated called Firebase demo that's exactly the same as all the others. But in this one, we're going to import the Firebase database. For this part, you need a Firebase account as well as AngularFire 2 installed. You can follow the official instructions on the main repo. I'm going to import the Firebase database and you could also use Firestore here as well if you wanted to. Then we'll need an RxJS observable as well as the take operator. First, I'm setting up a reference to the AngularFire object. Then we'll retrieve that data as an observable. Then we'll inject our SEO service as well as the AngularFire database in the constructor. To make the reference, we just call DBObject and I have this data saved on the demo node, and then we get the data by calling value changes. Let's quickly jump into Firebase and see what this data looks like. For this demo, it looks exactly like the data we had hard coded in the components earlier, but we're gonna load it over the network via Firebase. To set the Meta Tags, we're going to call take 1 on the observable And then subscribe to it and with the emitted value, we're going to use our SEO service to set the meta tags. The data we get back from the subscription will be that data we just looked at in Firebase, so we can set the title description and image meta tags. So that's all we need from Angular. Now it's up to Rendertron and Firebase Cloud Functions to make this SEO friendly. First, I'm going to show you how to deploy your own instance of Rendertron so you can use this in production. Google may offer this as a service in the future and I hope that they do, but for now we have to deploy our own instance of Rendertron. For testing, this part is completely optional. So if you wanna skip this step, you can go straight to the Firebase Cloud function. For this demo, I'm just going to clone Rendertron directly into my Angular project, but you'd probably wanna have it as a standalone project separate from Angular. I'd also like to point out that you need to have Docker installed. The system requirements vary, so I'm not able to show you that step by step here. The next step is to CD into that directory, then run the Docker build command. It will take a few minutes to build the container, but once it's finished you should be able to run Docker run and serve it locally on your system. If you navigate to port 8080, you should see the Rendertron app running there. This is great if you want to customize Rendertron source code for your specific needs. If you already have a project with Firebase, you can deploy the container to App Engine with a single command. You'll first wanna go into the app YAML file and set the number of CPU cores and memory that you wanna used. If you have the Google Cloud Platform command line tools installed, you can simply call gcloudapp deploy with your corresponding Firebase project. That's going to take another few minutes, but it'll give you back a URL with your own custom production ready version of Rendertron. Now we're ready to create the Cloud Function middleware that will determine if the user is a bot or an actual human. You'll need both Firebase Hosting and Firebase Functions initialized in your project. Then we need to configure hosting to use the Firebase Cloud Function for any URL other than the root URL. So So we do that by adding it to the rewrite section and the function is going to be called app. But before we get into the function, I'd like to point out that Rendertron has an existing Firebase Cloud function designed to work with Polymer. It's not compatible with Angular, but it might be worth checking out. So getting back to our app, we're going to CD in the functions directory. Then we're going to install Express JS, which will give us some syntactic sugar for routing and also a library called Node Fetch. Then we're gonna go into the index JS file and import these required libraries. We're also going to import URL from Node and then set our express app as a variable. Then I'm going to set our URL endpoints directly in the function itself, but you could also set these as environment variables. So the app URL is going to be the URL that we deploy our Angular app to in Firebase Hosting And then the render URL is the Rendertron endpoint. So you can use this Rendertron endpoint exactly as it appears here. But if you deploy your own, then you would use your own render URL. So we'll export a function called app and that's going to give us access to a request and response object, and we're going to use the wildcard character to catch all routes. The first thing I'm going to do is write a couple of helper functions to make this a little bit easier. So first, we need to generate the URL from the request. So it won't be in the correct format unless we use the Node URL library. So we'll get the protocol, the main app URL, Then path name is the actual route the user is trying to get to. To detect search engine crawlers and social media bots, we're going to create a big list of known bot user agents. So probably the most famous is Googlebot, but Googlebot's good at executing JavaScript so you may not even want to include it here. Then we'll also set up our social media bots that would be like Twitter bot, Facebook, LinkedIn, Slack, and many others. This list isn't comprehensive. You can definitely add additional values here to detect other bots. The way we actually detect a bot is from its user agent header in the request. So we're going to take that header and convert it to lowercase. And then we're going to loop over this list of Bots. If we detect the substring in the header, then we're going to say the bot was detected. We can console log it for debugging and then we'll return true from the function. Otherwise, we're just going to return false. When the Cloud function is invoked, the very first thing we're going to do is run this DetectBot helper with the request header. If it returns true, we'll know that we need to send this request to Rendertron first. So if that's the case, then we're going to format the URL properly to send it to our Rendertron Tron Endpoint. We can do that with the generate URL function we created earlier. And then we'll call fetch to fetch the actual URL. So we call the render URL slash bot URL. That returns a Promise, then we'll get the text from that Promise. That will give us a response body that we can send back to the actual crawler bot, but we're going to define some caching on it. So we can set the cache control header, then we'll set the very header which signals which part of the request was important in determining our response. In this case, the user agent header is what determines how we build this response. The last thing we'll do is convert the body to a string, and then we can send that as a response from the Cloud function. That will be a fully rendered and serialized web page that can be used by the actual crawler bot. If we don't detect a bot, then we just want to display the main Angular app. We can do that by simply fetching the root URL that's deployed to Firebase. We can handle the response in the same exact way. We'll just turn the body into a string and then send that as a response from the Cloud function. You could probably optimize this to be faster, but it's only going to fire when you don't go to the root URL. Because of Firebase Hosting Priorities, It will use the index HTML file when you navigate to the root URL on your project. I discussed this in more detail in the main article, But for now, we're just going to go ahead and deploy the function. So run Firebase deploy only functions. Then you'll also need to create a production build for your Angular app. So run ng build production, then run firebase deploy only hosting. Just like magic, your progressive web app is now optimized for search engines and can be used by link bots, and you didn't have to break your Angular app in the process. All of this is very cutting edge, so I recommend you check out my working demo and reach out on Slack if you have any questions. For each page in the demo, there's a URL, and if you paste that into any social media outlet, you should get a link preview. Or, if you fetch it as a bot, you should see that fully rendered HTML. That's it for Angular SEO with Rendertron. If this video helped you, please like and subscribe. And, if you want advanced Exclusive content related to these topics, consider becoming a pro subscriber at angularfirebase.com. You'll get a free copy of my book as well as 1 on 1 project consulting. Thanks for watching and I'll see you soon.
The speaker shows a demonstration of a Angular 5 web app designed to run on any browser and is specifically designed for search engines and Cloud functions. They demonstrate how to use the app in a new project and recommend a Dockerized version for production use. The speaker also explains how to generate meta tags and set up links and URL paths for each component, and how to deploy the app in Angular. They stress the importance of optimizing the Angular app for search engines and recommend using a free copy of their book and a one-time project consulting.