Rendering Static Pages from Dynamic Routes in Angular with Scully
Tags: angular, javascript, scully, jamstack
Published: 2020-05-13 14:27:31

---
title: 'Rendering Static Pages from Dynamic Routes in Angular with Scully'
published: true
description: Learn how to server side render an Angular application with routes using Scully.
tags: Angular, javascript, Scully, JAMstack
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/u9vlwjucuc11dw62vmbh.jpg
canonical_url: https://jenniferwadella.com/blog/rendering-static-pages-from-dynamic-routes-in-angular-with-scully
series: scully 
---

## JAMStack 101

As JavaScript applications grow in complexity and offer users robust features, performance becomes an increasing growing concern. JAMstack is an approach to building websites with performance in mind, regardless of the JS framework under the hood. The JAM in JAMstack stands for JavaScript, APIs, and Markup. Conceptually, this means the applications' functionality is handled by JavaScript, server side interactions utilize reusable APIs and the application pages are served as static HTML files, versus content that is injected into the page dynamically by JavaScript as is usual with single page applications. 

## What is Scully?

[Scully](https://scully.io/) was created with the intent of being the piece of the JAMstack puzzle Angular developers have been craving. Scully makes it easy to statically generate pages to be served for modern (v8 or v9) Angular applications. 

## Server Side Rendering

In order to serve content quickly, static application pages must be generated, served to the browser, and then be bootstrapped by the client. This allows applications to be painted in the browser faster, because we're not waiting on all of our JavaScript to load and then execute, with interactivity not far behind. When this happens on the server, this approach of pre-rendering and serving HTML pages is called server side rendering. Scully takes care of the generating static pages for us. 

## Getting Started with Scully

Let's take a look at implementing generating static pages with Scully on this example Restaurant Ordering application, Place My Order. This is a demo application where a user can search for restaurants based on city and state, choose a restaurant, and place an order from that restaurant. You can view the demo code on github here: https://github.com/tehfedaykin/pmo-starter

### Add Scully to Your Project

To get started with Scully run in the root directory of your Angular application:

```ng add @scullyio/init```

This will install Scully, import the `ScullyLibModule` to your root app module, and generate the Scully config file (`scully.{{yourApp}}.config.js`) in your root directory. 

Scully starts with the routing module of your Angular application to determine the pages to pre-render, then offers ways to specify other routes to render in the config file. This means if you don't have a routing module, or if you have no routes written, Scully will not render anything. 

In the example Place My Order app, the router contains the following routes: 

```typescript
const routes: Routes = [
  {
    path: '',
    component: HomeComponent,
  },
  {
    path: 'restaurants',
    component: RestaurantComponent,
  },
  {
    path: 'restaurants/:slug',
    component: DetailComponent,
  },
  {
    path: 'restaurants/:slug/:order',
    component: OrderComponent,
  },
  {
    path: 'order-history',
    component: HistoryComponent,
  }
];
```

### Prepare Application Code for Scully

To start using Scully, you must first run:

```ng build```

This will build your Angular application files and put them in the `dist` directory, unless you have a custom build directory output. Be aware that Scully will look for the compiled app files in the `dist` directory to do its magic.  

### Generate Pre-rendered Pages

Once you've built your latest code to the `dist` directory, run: 

```npm run scully```

This will render the static pages of your app based on your app router and the Scully config file. 

Let's take a look at the output and files generated: 

```
No configuration for route "/restaurants/:slug" found. Skipping
No configuration for route "/restaurants/:slug/:order" found. Skipping
Route list created in files:
  ".../place-my-order/src/assets/scully-routes.json",
  ".../place-my-order/dist/static/assets/scully-routes.json",
  ".../place-my-order/dist/place-my-order/assets/scully-routes.json"

Route "" rendered into file: ".../place-my-order/dist/static/index.html"
Route "/restaurants" rendered into file: ".../place-my-order/dist/static/restaurants/index.html"
Route "/order-history" rendered into file: ".../place-my-order/dist/static/order-history/index.html"
```

Inside the scully-routes.json file we see a list of application routes:

```json
[{"route":"/"},{"route":"/restaurants"},{"route":"/order-history"}]
```

Notice that the `restaurants:slug` routes aren't included, and we received a message telling us no configuration for these routes was found. Because these routes are dynamic, we need to give Scully a little help with how to render a page like `restaurants/jennifers-tasty-brunch-cafe`. 

If our application had SEO goals of making our restaurant pages searchable so potential customers could find their new favorite brunch spot, we'd absolutely want to make sure we were statically generating them for search engines including meta information used by sites like Twitter, Facebook, and Linked In. 

One way we could take this is my manually listing all the possible routes in the Scully config file. 

```typescript
exports.config = {
  projectRoot: "./src",
  projectName: "place-my-order",
  outDir: './dist/static',
  extraRoutes: [
    '/restaurants/jennifers-tasty-brunch-cafe',
    '/restaurants/Q39',
    '/restaurants/novel',
    '/restaurants/local-pig',
    '/restaurants/shio-ramen'
    ...
```

But this would get tedious in a hurry, and what if restaurant owners were allowed to submit their own restaurant to be included in our app? We wouldn't want to have to update our config file every time. Fortunately Scully provides great ways to handle custom situations. 

### Scully Plugins

The Scully authors created a plugin system that allows us to have more control over how Scully renders our applications, and even provided a few built in for us. <3

Using plugins will allow us to create dynamic routes and render those dynamic static pages instead of waiting for the page to load when the user navigates.

The built-in JSON plugin will help us do exactly this. To work the JSON plugin requires a route and a configuration obj with an API url and key to be used for the route, and will return a list of generated routes for the data returned by the url. This means we can use our API to create the list of routes.

In the Scully config file there is a routes prop that accepts the following `routeConfig` interface(*** I abstracted this a bit from the Scully source code so we can see what we need for using the JSON plugin): 

```typescript
interface RouteConfig {
  '[route: string]': {
    type: 'json';
    postRenderers?: string[];
    [paramName: string]: {
      url: string;
      property: string;
      headers?: HeadersObject;
      resultsHandler?: (raw: any) => any[];
    };
  };
}
```

To dynamically create our restaurant routes, we need to pass the route with the dynamic param, our API url, and the key we want from our data. The JSON plugin will map over the data response from the API url we pass it and return the key of each object, in this case 'slug'. 

```typescript
exports.config = {
  projectRoot: "./src",
  projectName: "place-my-order",
  outDir: './dist/static',
  routes: {
    '/restaurants/:slug': {
      type: 'json',
      slug: {
        url: 'http://www.place-my-order.com/api/restaurants',
        property: 'slug',
      },
    },
  },
};
``` 

However, my endpoint response looks like this, with my array of restaurant data nested inside a 'data' key: 

```typescript
{
  data: [
      ...restaurants
  ]
}
```

Scully provides an optional `resultsHandler` method we can use to map the response of our API to an array to be iterated over:


```typescript
exports.config = {
  projectRoot: "./src",
  projectName: "place-my-order",
  outDir: './dist/static',
  routes: {
    '/restaurants/:slug': {
      type: 'json',
      slug: {
        url: 'http://www.place-my-order.com/api/restaurants',
        resultsHandler: (response) => response.data,
        property: 'slug',
      },
    },
  },
};

```

Now when `npm run scully` is run all of the `/restaurants/:slug` routes have been added to the scully-routes.json file and the files have been dynamically created in the `dist/static` dir! Yay!

### Serving 

To serve the statically generated assets run:

```npm run scully serve```

You'll be able to view your speedy app at `http://localhost:1668/`. Cheers!