Chapter 2. Introducing the JAMstack

What’s in a Name?

The JAMstack at its core, is simply an attempt to give a name to a set of widely used architectural practices. It gives us one word to communicate a large range of architectural decisions.

It came about in conversations between people involved in the communities around static site generators, single-page application (SPA) frameworks, build tools, and API services as we realized much of the innovation in each of these categories was connected.

An entire community grew up around the first mature generation of static site generators like Jekyll, Middleman, Metalsmith, Cactus, and Roots. Jekyll found fame as the static site generator supported natively by GitHub Pages, and others were developed in-house by agencies or startups that made them a core part of their website stack. The tools brought back a feeling of simplicity and control from the complex database-based Content Management Systems (CMSs) that they replaced.

At the same time, single-page app frameworks like Vue and React started a process of decoupling the frontend from the backend, introducing build tools like Grunt, Gulp, and later, Webpack and Babel, which all propagated the idea of a self-standing frontend with its own build pipeline and deployment process. This brought a proper software architecture to the frontend workflow and empowered frontend developers to iterate much faster on the user interface (UI) and interaction layer.

When these approaches to building websites and applications intersected, a new API ecosystem began to emerge. Tools like Stripe, Disqus, and Algolia made payments, comments, and search available directly from the frontend via API-driven services.

A large set of terms for the different pieces of these new software architectures began to appear: progressive web apps, static sites, frontend build pipelines, single-page applications, decoupled web projects, and serverless functions. However, none of them really captured the move to a new architecture that encompassed content-driven websites, web applications, and all the interesting hybrids between the two.

The JAMstack put a name to all of it.

In today’s web development environment, the “stack” has moved up a level. Before, we used to talk about the operating system, the database, and the web server when describing our stack (such as the LAMP stack and MEAN stack), but with the emergence of new practices, the real architectural constraints became the following:

  • JavaScript in the browser as the runtime

  • Reusable HTTP APIs rather than app-specific databases

  • Prebuilt markup as the delivery mechanism

These components became the JAM in JAMstack. Let’s take a look at each of these elements and how they’ve each evolved to enable this new architecture.

JavaScript

In May 1995, when Brendan Eich wrote the first prototype of JavaScript (originally called Mocha) during a famous 10-day coding spree, few could have imagined that it would someday be the most important runtime language for the widest-reaching publishing stack ever.

Today, JavaScript is nearly omnipresent in browsers. It has grown from Eich’s initial creation of a small, scheme-inspired language with a C-like syntax into the most highly optimized interpreted language in the world. It includes advanced constructs for asynchronous actions, flexible class and module system, elegant syntactic constructs like destructuring assignment, and an object syntax that’s become the most commonly used data exchange format on the web and beyond (JSON).

More than just a standalone programming language, JavaScript has become a compilation target for transpilers, which translate variations of JavaScript into vanilla JavaScript runnable in all modern browsers, and compilers for new or existing languages like Elm, ReasonML, ClojureScript, and TypeScript.

In many ways JavaScript is now the universal runtime that Sun Microsystems dreamed of when it built the Java Virtual Machine (JVM).

This “Virtual Machine” for the web is the runtime layer for the JAMstack. It’s the layer we target when we need to create dynamic flows that go above the content and presentation layer, whether we’re writing fully fledged applications or just adding extra functionality to a content-based site. JavaScript is to the JAMstack what C was to Unix, as the browsers have become the operating system of the web.

APIs

The World Wide Web is fundamentally just a UI and interaction layer built on top of a stateless protocol called the HyperText Transfer Protocol (HTTP). The most fundamental concept of the web is the Universal Resource Locator (URL).

As humans, we’re used to being able to type a URL into a browser or follow a link from a website and end up on another website. When building websites or applications, we should always think carefully about our URL architecture and structure. But URLs also give programs running in a browser the power of reaching any programmatic resource that’s been exposed to the web.

Modern web APIs were officially born with Roy Fielding’s dissertation, “Architectural Styles and the Design of Network-Based Software Architectures,” in 2000. It is a seminal paper that defined Representational State Transfer (REST) as a scalable and discoverable architecture for services exposed through HTTP.

The first web APIs were meant to be consumed from server-side applications. Without using tricks like proxying through an intermediary server or using plug-ins like Flash or Java Applets (ugh!), there was no way for a program running in a standard browser to talk to any web API outside of its own domain.

As JavaScript grew from a scripting language mainly meant to do minor progressive enhancements on top of server-side rendered websites into a fully fledged runtime environment, new standards like CORS, WebSocket, and PostMessage emerged. Paired with other new standards like OAuth2 and JSON Web Token (JWT) for authorization and stateless authentication, any modern web API suddenly became reachable from any JavaScript client running in a browser.

Only recently have we begun to understand the effects of this massive innovation. It is one of the drivers propelling the JAMstack as one of the most relevant architectural styles for websites and applications. Suddenly, all of the web has become something like a giant operating system. We’re now seeing APIs exposed for anything—from payments or subscriptions over advanced machine learning models running in massive cloud environments, to services that directly affect the physical world like shipping or ride-sharing services, and just about anything else you can imagine. Think it up, and there’s probably some API for it out there.

Markup

At the core of the web is HTML. The HyperText Markup Language, known and understood by all browsers, defines how content should be structured and distributed. It defines which resources and assets should be loaded for a site and presents a Document Object Model (DOM) that can be parsed, presented, and processed by anything from standard web browsers over screen readers, to search engine crawlers, to voice-controlled devices or smart watches.

In the early days of the web, a website was simply a folder with HTML files exposed over HTTP by a web server. As the web evolved, we began moving to a model in which a running program on the server would build the HTML on the fly for each visit, normally after consulting a database.

This was a much slower, more complex process than serving static assets, but it was also the only viable way to work around the fact that browsers were simple document viewers at the time. Responding to any user interaction, be it a click or form submission, required an entirely new page of HTML to be assembled on the server. This was true whether you were building a comments feature for a blog, an ecommerce store, or any kind of web application. Because of this document-centric architecture, web experiences often felt much less responsive than desktop applications.

Of course, this was before the rise of JavaScript and the emergence of cross-domain web APIs available from the browser. Today the constraints that led to the legacy architectures of the web have gone away.

Prebuilding Markup

Markup on the JAMstack is delivered by a different model. It is not delivered from traditional frontend web servers tasked with building pages at runtime.

Instead, the JAMstack approach is to prebuild all the markup up front and serve it directly to the browser from a CDN. This process typically involves a build tool (sometimes a static site generator like Hugo or Gatsby; sometimes a frontend build tool like Webpack, Brunch, or Parcel) where content and templates are combined into HTML, source files are transpiled or compiled into JavaScript, and CSS runs through preprocessors or postprocessors.

This creates a strict decoupling between the frontend and any backend APIs. It allows us to remove a large set of moving parts from the infrastructure and live system, which makes it much easier to consider the frontend and individual APIs involved in isolation. The more we can bake, not fry, the content layer of our site, the more simple deployments, scaling, and security become, and the better the performance and end-user experience will be.

In many ways this decoupled architecture is similar to the architecture of mobile apps. When the iPhone introduced mobile apps (originally because traditional web apps were not performant enough), it was simply not a consideration to build a model in which the full UI would be reloaded from a server every time the user took some action. Instead, IOS introduced a model in which each app could be distributed to users as an application package with a declarative UI, including any needed assets to communicate with JSON or XML-based web APIs from there.

The JAMstack approach is similar. The frontend is the app. It is distributed to browsers directly from a CDN on a globally distributed network. Through techniques like service workers, we can even install our frontend directly on end users’ devices after their first visit.

Microservices and serverless functions

Another thing happened in tandem with starting to split the frontend and API layers: microservice-based architectures. Backend teams began moving away from one large monolithic API toward much smaller services with more narrowly defined responsibilities. This move toward microservices has culminated in Amazon Web Services (AWS) Lambda and the concept of cloud functions, whereby simple functions doing just one thing and nothing more can be exposed as microweb API endpoints.

This has triggered an important architectural shift. Before, a typical SPA had just one API through which it would communicate. Now each SPA will typically communicate with many different APIs or microservices. As the surface area of each of these APIs shrinks, and standards like OAuth and JWT allow developers to tie them together, the individual API tends to become more and more reusable.

The amount of fully managed, reusable APIs available as managed services will only keep growing. We’re now beginning to see the first function app stores, helping to lift the burden of maintenance involved in running a modern application.

Types of JAMstack Projects

Development teams all over the world have deployed a wide variety of JAMstack-powered projects, from smaller sites and landing pages to complex web applications, to large enterprise web properties with thousands of pages.

HTML Content

The simplest JAMstack sites are pure static sites: a folder with HTML and supporting assets (JavaScript, Cascading Style Sheets [CSS], images, font files) and no moving parts; plain-text files that can be edited directly in an editor of choice and kept under version control.

As soon as there is much more than a single page, however, it makes sense to extract common elements like navigations, headers, footers, and repeated elements into their own templates and partials. For anything but the simplest JavaScript, you can pull modules from npm and use more modern ES6 features. After you have a slightly more complex stylesheet, you can introduce automatic vendor prefixing when needed, support for CSS variables across browsers, and nesting of rules.

A proper build tool chain solves all of these while keeping the basic simplicity of a static site. Everything lives in text files in a Git repository, is under version control, and can be manipulated with all of our text-centric developer tools. With a modern continuous deployment (CD) workflow, publishing is as simple as a Git push.

Content from a CMS

For most of the life of the web, we’ve taken for granted that our CMS determines our entire development platform and couples rendering, content editing, and our plug-in ecosystem tightly together. But the advent of headless CMSs—whether they are API-driven or Git-based—means that we can think of content editing UIs as completely separated from the frameworks and tools which we use to render and deliver our websites.

Content management can also be handled by Git-centric content editing tools like Netlify CMS, Prose, Forestry, or CloudCannon. Or, you can use the build tool to make the first build step a content synchronization with an external content API like Contentful, Prismic, or DatoCMS.

Web Applications

In the early 2000s, Microsoft discretely added a homegrown API to Internet Explorer called XMLHttpRequest (XHR). Before this happened, the only way a browser could initiate a page load was by navigating to a new URL that would load a completely new web page from scratch.

After years of XHR going more or less unnoticed, Jesse James Garrett from the agency Adaptive Path wrote an article in February 2005 that would change the web: “Ajax: A New Approach to Web Applications.” In the article, Jesse coined the term “Ajax” and gave web developers a way to communicate about the new patterns that emerged after XHR was used to fetch data from servers directly from JavaScript—similar to how the JAMstack terminology now lets us talk about a new architecture under the banner of a unifying nomenclature.

In today’s world of ubiquitous frontend-heavy web applications, it’s difficult to absorb just how revolutionary it was to move from relying on complete page refreshes as the only way of truly interacting with a web application to suddenly having the option of initiating HTTP requests straight from JavaScript. The realization that Ajax requests could be used to build web applications without the reliance on full page loads changed the way we thought about the web and also pushed the browser market to start fundamentally reinventing itself after years of IE6-based stagnation.

Initially, Ajax was mostly used with principles like progressive enhancement in which a web application would be built in a way that all state was handled on the server through full page transitions but where browsers with JavaScript support could enhance the interactions through partial page refreshes instead.

Separating the frontend from the backend

True SPAs came about when JavaScript performance became good enough that we could invert the existing model and handle all page transitions directly in the browser without any server-side round trips. The first generation of SPAs was typically built by developing a frontend inside large, monolithic database-driven applications. It was common to work on stacks with Ember frontends inside Rails apps, using the Rails asset pipeline for bundling and minification (even if this flow was mostly painful to work with).

Modern SPAs began to separate the frontend and backend completely, and a whole new generation of frontend build tools emerged to make working with the frontend in isolation truly joyful—after the initial setup and configuration hurdles have been overcome, of course. Webpack is the most commonly used build tool for SPAs today, offering a flow that supports JavaScript and CSS transpiling, postprocessing, integrated bundling and code splitting, as well as real-time browser refreshes on any save.

Large Web Properties

When it comes to prebuilding markup, there are obvious constraints in terms of the number of pages it is practical to generate. The build and deployment cycle simply becomes too long to allow for a viable workflow. If you need to publish a story before a competitor, but you have a one-hour build and deployment cycle between the moment you press publish in your CMS and the moment the story is live, your stack has fundamentally let you down.

For some kinds of publishing, this is such a tight constraint that even a 10-minute build and deploy cycle would be better served by another stack work.

However, build tools have become surprisingly fast, and the boundaries of what can be achieved with a build cycle is shrinking. A static-site generator like Hugo can process thousands of pages a second and provide much faster build cycles. Such impressive performance improvements in these tools, along with emerging techniques to tackle very large sites by carefully splitting site generation into separate branches so that only a small subset of the site is rebuilt for each update, combine to provide great results.

In the end, the trade-off will need to be based on what’s most important: performance, uptime, and scalability, or shortening the publishing cycle.

For the majority of content-driven sites, as long as a deploy can be scheduled to go live at a certain time, being able to run a build cycle in less than 10 minutes is likely not of real importance. Unless your site is truly gigantic (pages counted in millions rather than thousands), a JAMstack approach might be a more viable option than you would have once thought.

Hybrid Experiences

It’s common for a company to require a variety of web experiences: a primary website, a blog, a documentation site, a web application, an ecommerce store, and so on. Maintaining a common look and feel across all these applications used to be extremely challenging for web teams. That’s because on traditional architectures, the frontend of each application is tightly coupled to the backend used to generate it on the server.

The JAMstack solves for this in an elegant, highly scalable way. We can build one, decoupled frontend that spans across all applications. You’ll see this in action later when we present the Smashing Magazine case study.

Summary

The list of projects that wouldn’t be a fit for a JAMstack architecture is continually getting shorter. Improvements in the tooling and automation, the workflows, best practices, and the ecosystem as a whole are creating more possibilities for what a JAMstack site can achieve.

Now that we’ve covered what the JAMstack is and whether it’s right for your project, it’s time to talk about the advantages that the JAMstack approach can provide.