Prerender an Angular app
Tags: javascript, angular, typescript, tutorial
Published: 2020-11-29 17:32:19



Hello 👋 ! Not so long ago, I meet the need to prerender an Angular application and I taught it will be nice to share it with you.


Let's check step by step, how to create and prerender a new Angular app. 

If you are interested to prerender an existing app, You can jump to step 3. 😉

> #### FYI: You can also clone this example on [Github](https://github.com/Maj07/angular-prerender-example) 👈

### 1. New project 
Let's create a new angular project with [Angular Cli]((https://angular.io/cli)) 

```bash
ng new angular-prerender-test
```

### 2. Create some routes

For the example I will create 3 routes :
- `/` : Home page (static route)
- `/contact` : Contact page (static route)
- `/user/:id` : User profile (dynamic route), content will be different for each id

You can create your components via Angular Cli with : 
```bash
ng g c YourComponentName
```

Here is what my components looks like: 

```typescript
// home.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-home',
  template: `<h1>Home Page</h1>
    <p>Hello World, welcome to the home page</p> `,
  styles: [],
})
export class HomeComponent{
  constructor() {}
}

// contact.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-contact',
  template: `<h1>Contact</h1>
    <p>You can contact me on : +1 *** *** *** *23</p>`,
  styles: [],
})
export class ContactComponent {
  constructor() {}
}

// user.component.ts
import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-user',
  template: `<h1>User: {{ id }}</h1>
    <p>This is {{ id }}'s profile</p>`, // 👈 user id in template
  styles: [],
})
export class UserComponent {
  id = '';

  constructor(private route: ActivatedRoute) {
    // Get param from route
    this.route.params.subscribe({ next: (res) => (this.id = res.id) });
  }
}


```

and your `app-routing.module.ts` should be like : 

```typescript
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ContactComponent } from './contact/contact.component';
import { HomeComponent } from './home/home.component';
import { UserComponent } from './user/user.component';

const routes: Routes = [
  /* Home page */
  {
    path: '',
    component: HomeComponent,
  },
  /* Contact page */
  {
    path: 'contact',
    component: ContactComponent,
  },
  /* User profile page */
  {
    path: 'user/:id',
    component: UserComponent,
  },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}
```
So now when you run your project with `npm start`, you should have 3 pages 

- http://localhost:4200 => home page
- http://localhost:4200/contact => Contact page
- http://localhost:4200/user/bob => bob's profile page

### 3. Install Angular Universal
Now that we have our project configured, we can move on and install Angular Universal.

```bash
ng add @nguniversal/express-engine
```

If you open your `package.json`, you should find a new script : 
```json
"prerender": "ng run angular-prerender-example:prerender"
```
### 4. Static routes
To prerender static routes, it's pretty straightforward, run : 

```bash
npm run prerender
```

If you check the build, you should have something like : 
```bash
dist/angular-prerender-example/browser
├── 3rdpartylicenses.txt
├── contact
│   └── index.html # 👈 contact page
├── favicon.ico
├── index.html # 👈 home page
├── index.original.html
├── main.271dcd2770e618160ca0.js
├── polyfills.bf99d438b005d57b2b31.js
├── runtime.359d5ee4682f20e936e9.js
└── styles.617af1cc16b34118b1d3.css
```

If you open those file you are going to have : 
```html
<!-- index.html -->
...
<div _ngcontent-sc36="" class="container">
  <router-outlet _ngcontent-sc36=""></router-outlet>
  <app-home>
    <h1>Home Page</h1>
    <p>Hello World, welcome to the home page</p>
  </app-home>
</div>
...

<!-- contact/index.html -->
...
<div _ngcontent-sc36="" class="container">
  <router-outlet _ngcontent-sc36=""></router-outlet>
  <app-contact>
    <h1>Contact</h1>
    <p>You can contact me on : +1 *** *** *** *23</p>
  </app-contact>
</div>
...

```

Tada ! Our static routes are prerendered ! 🎉

But, wait what about my dynamic route `/user/:id` ?!? 🤔


### 5. Dynamic routes
For dynamic routes, we should define which routes we want to prerender. For it, we need to create a new file `user-routes` at the root of the project and list all routes you want. 

> You can name the file as you like


Example : 
```txt
/user/Joan
/user/Sherry
/user/Frank
/user/Bob
```

Let's open `angular.json`. 

In `prerender` section add a new attribute `routesFile` with your file name.

```json
...
"prerender": {
  "builder": "@nguniversal/builders:prerender",
  "options": {
    "browserTarget": "angular-prerender-example:build:production",
    "serverTarget": "angular-prerender-example:server:production",
    "routes": [
      "/"
    ],
    "routesFile" : "user-routes" // 👈 add your file name
  },
  "configurations": {
    "production": {}
  }
}
...
```

Then run :
```bash
npm run prerender
```

Let's check the output: 

```bash
dist/angular-prerender-example/browser
├── 3rdpartylicenses.txt
├── contact
│   └── index.html
├── favicon.ico
├── index.html
├── index.original.html
├── main.271dcd2770e618160ca0.js
├── polyfills.bf99d438b005d57b2b31.js
├── runtime.359d5ee4682f20e936e9.js
├── styles.617af1cc16b34118b1d3.css
└── user
    ├── Bob
    │   └── index.html # 👈 
    ├── Frank
    │   └── index.html # 👈 
    ├── Joan
    │   └── index.html # 👈 
    └── Sherry
        └── index.html # 👈 
```
Let's open one of those files :
```html
<!-- user/bob/index.html -->
...
<div _ngcontent-sc36="" class="container">
  <router-outlet _ngcontent-sc36=""></router-outlet>
  <app-user>
    <h1>User: Bob</h1>
    <p>This is Bob's profile</p>
  </app-user>
</div>
...
```
and that's it, routes listed in `user-routes` are prerendered ! 🎉

Hope it helped some of you. 
Thanks for reading. 😇

Source code available on [Github](https://github.com/Maj07/angular-prerender-example) 👈