When you add anything with user-generated content or dynamic data to a static site, the complexity of the build process can become comparable to launching a monolithic CMS. How can we add rich content to static sites without stitching together multiple third-party services?
For people in the development community static site generators are a popular choice over traditional content management systems (CMS) like WordPress. By comparison static sites are usually lightweight, highly configurable, fast, easy to use and can be deployed almost anywhere.
With static websites, no code is generated on the server; we’ve replaced databases and server-side code with APIs and build processes.
Despite my enthusiasm, I’m often disheartened by the steep complexity curve I typically encounter about halfway through a JAMstack project. Normally the first few weeks are incredibly liberating. It’s easy to get started, there is good visible progress, everything feels lean and fast. Over time, as more features are added, the build steps become more complex, multiple APIs are added, and suddenly everything feels slow. In other words, the development experience begins to suffer.
It usually looks something like this:
One of the reasons for this steep rise in complexity is there are limits to the type of data that markdown can easily represent. Relationships are one example where static sites struggle. Relationships between pages or collections of assets (such as an image gallery) can only be represented by markdown in inefficient ways. It requires significant preprocessing to resolve anything more complicated than a simple set of tags or categories. If you’ve ever had to do it, you will also know the authoring experience of managing relationships in markdown isn’t ideal.
User-generated content is another area that can cause a steep rise in the complexity of static sites. Adding features like comments, ratings, likes or any other kind of dynamic content will require third-party services — each has its own account that needs to be managed, not to mention that adding third-party scripts can have a negative impact of page performance.
If a service doesn’t match your specific requirements, sometimes it’s possible to cobble solutions together using generic platforms like Google Forms or AirTable.
The end result is we’ve outsourced the database, fragmented the content management experience and stitched together a bundle of compromises. That’s a stark contrast from the initial ease of setting up and deploying a JAMstack site.
Although this complexity curve is not unique to JAMstack projects, adding rich features to markdown-driven sites is far more difficult than we care to admit. What happened!? A lack of complexity was one of the reasons we favored JAMstack in the first place.
We did that thing that web developers do. We moved the complexity from one space into another and congratulated ourselves 😂. Not having complexity on the server-side is good for front-end performance, but there is little incentive to optimize any further once we do this. Ridiculous build times and complicated tool-chains have become an acceptable reality for modern front-end web development.
Before I come across as sounding too critical, I should make it clear that I absolutely love static site generators. I think they are a perfect solution for many simple sites and you should still use them. However, I feel like a simple content management layer that I own and can configure is preferable to:
- poor content management experiences,
- complicated integration of third-party services, and
- inefficient build processes.
I want to combine the benefits of a CMS with static site generators.
And it seems I’m not the only one who has reached this conclusion:
WordPress (et al) is often pitted _against_ static site generators.
I get it. They feel like different worlds.
But SSG’s just take input and make static output. You may still want/need a CMS, and WordPress can be that. It has APIs that a SSG can injest.
— Chris Coyier (@chriscoyier) June 11, 2019
My encounters with static site generators lead me to the opinion that they are incredibly compelling as a way to work, but be sure you really do want a static site. Too often what I see is a messy compromise to deal with stuff a DB would make easy.
— Rachel Andrew (@rachelandrew) September 20, 2019
WordPress is a fantastic and familiar content editing experience, but what if you could deploy your site statically for ultimate speed, stability, and security? ⚡️You can, and here’s a great rundown of plugins for doing headless WordPress on Netlify: https://t.co/lz6fscbX3q
— Netlify (@Netlify) July 30, 2019
The solution doesn’t need to be another third-party service or require abandoning static sites entirely. You can use a personalized content management layer and unified API to enrich a static site. It might not be as hard as you think.
The first step is to create an API for your site. You can use any headless CMS, but the challenge I’ve had with many options is they make a lot of assumptions about the type of content you want. You might not want the CMS to manage pages and posts, but rather use it to store comments or images. I find this particularly difficult with WordPress. I often feel like I’m forcing a blogging platform to be just the service I need.
The new version of KeystoneJS (Keystone 5) is an excellent alternative to more opinionated content management systems. It’s made up of tiny independent components, so you only add the parts you need. This means it doesn’t feel like modifying a blogging platform. Instead it’s like creating a personalized mini-CMS and API to work specifically with your site.
I call this approach JAMstack Plus.
To help you get started with this idea I’ve created two projects:
- Supermaya, a starter kit for the static site generator Eleventy.
- Keystone JAMstack Plus, a blog enrichment platform.
The first project I want to share with you is Supermaya, an Eleventy starter kit designed to help add rich features to a blog or website without a complicated build process.
It comes with the all “blog standard” features including:
- Posts and Pages
- RSS feed
- Service worker
- Lazy loading images
- Critical CSS (if enabled)
It also has considerate and accessible markup. If deployed correctly, it should get full scores on a lighthouse audit out-of-the-box:
After this, I realized the other reason for increasing complexity on JAMstack sites was integration with third-party services, usually for user-generated content. To solve this, Supermaya has optional tie-ins with a Keystone JAMstack Plus starter-kit, which makes it easier to add user-generated content and other rich features.
You can deploy both Keystone and Supermaya together and connect them at the same time by following the instructions during installation. This will deploy Keystone to Herouku and Supermaya to Netlify, as well as configure your admin user and API URL.
Rich features are added with progressive enhancement, so if the API cannot be reached or there is a server error, the site will continue to function without noticeable degradation or delays for users.
JAMstack Plus starter kit
The Keystone JAMstack Plus starter kit allows you to add rich features to a blog including:
- Reading list, and
Just like Supermaya, it can be used on its own. After it’s deployed, you’ll get access to an admin interface that allows you to create and manage content. You’ll also get a GraphQL endpoint that can be connected to Supermaya.
It’s configured with the intention of being a headless CMS for user-generated content. It expects pages and posts to be managed by a static site generator. However, with a little work — and following the examples in Supermaya — you can connect any front-end to the GraphQL API.
I’d encourage you to modify the starter-kit: Add additional features or provide content for pages directly from Keystone. If you add features that could be used by the rest of the community contribute back to the starter-kit and we can make it easier for everyone to add rich features to their sites without the need for third-party services.
Note: The automatic deploy will deploy to a free instance of Heroku. This will sleep periodically if not used which can result in slow API response times after periods of inactivity. You can upgrade to a hobby instance to avoid this.
Consider owning your own data
JAMstack and servers are not incompatible. There’s always a server (usually multiple) — it’s just a question of who owns it. If you are using any kind of third-party service, the chances are they own your account information, your content and collect user data.
Sometimes this might be an acceptable compromise compared with the overhead of deploying and managing a back-end service, but when the complexity of stitching together several APIs becomes comparable to a CMS, I believe managing a tiny configurable service that you own, can provide a better experience for users, developers and content managers. It also provides a solid platform for websites to grow beyond purely static content into more complicated and varying types of applications.
I don’t think JAMstack should defined by pushing all the complexity into the front-end build process or by compromising on developer and user experience. Instead, I think JAMstack should focus on providing lean, configurable static front-ends. These can be connected to APIs to provide user-generated data and content management services. There is no reason not to own and manage these services, if it provides the best outcome.