I created HipHip.app, a website and iPhone app that lets you create surprise celebration videos quickly and easily.

The website is written in Angular.

Angular is incredibly powerful, but two drawbacks until very recently were first-time download performance and search engine optimization. These are natural consequences of so-called “Single Page Application” (SPA) frameworks like Angular. Rather than render a new page on every request, SPA applications are written in JavaScript, downloaded in bigger chunks of code, and build out the user-interface for each route on the client-side.

To help return some of these benefits back to Angular developers, the Angular team has released Angular Universal, a “server side rendering” technology. But in my own experience, at least through Angular v.11, I’ve found that it’s not quite ready for prime-time, with a whole lot of workarounds needed for most common Angular components and libraries. Among other things, when you use Angular Universal, any use of the “window” object in your code or third-party components has to be rewritten or worked-around.

Enter Scully, a Fast Static Site Builder

But recently, a team of developers took a different approach and created Scully, which allows you to easily pre-render the majority of the routes on your website. That is, with Scully, at build/test/deploy time, a Puppeteer bot is spun up in an Express application, and crawls your ready-for-production site in a variety of configurable, smart ways. You end up with a bunch of static “index.html” pages that are all ready to go, along with a bootstrapper that loads in your Angular application after page download. The result is that a user and a search engine spider each see a very fast initial page with most of the necessary content, while the rest of the app is downloaded in the background.

It brings the JAMStack philosophy to Angular.

So, I’ve added Scully to HipHip‘s build & deployment stack, resulting in much faster load times for end-users. I’ve been happy with it.

And over time, it should also lead to better search engine optimization for the site.


If you’ve read this far, you probably already know what a sitemap is, but it’s a file which tells the search engines which links you’d like it to crawl. It’s a helpful asset to have in a Search Engine Optimization strategy, because it can point the search engines to the routes you most care about, and away from the ones you do not.

Rendering a Sitemap from Scully Output

Since Scully generates a scully-routes.json file (you’ll see it in your “assets” subfolder by default after Scully completes its work), the natural next step in the build process for better SEO is to simply walk through that JSON file and create a sitemap.xml.

Here’s some quick Python code to do just that, which I’ve added as a step in my validator/build routine. It builds valid sitemap.xml files, and you can use it as a starting point for other work if you’d like.

Sharing here in case other Angular developers find this useful.

I place this file below, generate-sitemap.py in my “src” subfolder, then I run this script after Scully completes its work in the build process. Error and exception handling left up to the reader. Obviously, change “https://hiphip.app” in the code below to your root URL.

# generate-sitemap.py
import json

def generate_sitemap():
    # the list of specific routes to ignore
    # the list of route prefixes to ignore 
    ignore_startswith=["/utils", "/dash", "/login", "/logout", "/create", "/account", "/error", "/settings"]

    print("Generating sitemap.xml from ./assets/scully-routes.json")

    with open('./assets/scully-routes.json') as f:
        data = json.load(f)
        f = open("sitemap.xml", "w")
        f.write('<?xml version="1.0" encoding="UTF-8"?>\n')
        f.write('<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n')

        for route in data:
            path = route["route"]
            if path not in ignore_list:
                if (not path.startswith(tuple(ignore_startswith))):
                    f.write('<loc>https://hiphip.app'+path+'</loc>\n') ## << CHANGE
                    print("SKIPPING: "+path)
                print("SKIPPING: "+path)

        print("SITEMAP GENERATED SUCCESSFULLY. Saved to src/sitemap.xml")

Write A Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.