Reconciling Editor Experience and Developer Experience in the CMS

Components are great, aren’t they? They are these reusable sources of truth that you can use to build rock-solid front-ends without duplicating code.

You know what else is super cool? Headless content management! Headless content management system (CMS) products offer a content editing experience while freeing that content in the form of data that can be ported, well, to any API-consuming front-end UI. You can structure your content however you’d like (depending on the product), and pull that content into your front-end applications.

Using these two things together — a distributed CMS solution with component-based front-end applications — is a core tenet of the Jamstack.

But, while components and headless CMSs are great on their own, it can be difficult to get them to play nicely together. I‘m not saying it‘s difficult to hook one up to the other. In a lot of cases, it’s actually quite painless. But, to craft a system of components that is reusable and consistent, and to have that system maintain parity with a well-designed CMS experience is a difficult thing to achieve. It’s that win-win combo of being able to freely write content and then have that content structured into predictable components that makes headless content management so appealing.

Achieving parity between a CMS and front-end components

My favorite demonstrating this complexity is a simple component: a button. Let‘s say we’re working with React to build components and our button looks like this:

<Button to="/">Go Home</Button>

In the lovely land of React, that means the <Button> component has two props (i.e. properties, attributes, arguments, etc.) — to and children. children is a React thing that holds all the content within the opening and closing tags, which is “Go Home” in this case.)

If we’re going to enable users in the content editor to add buttons to the site, we want a system for them that makes it easy to understand how their actions in the CMS affect what appears on screen in the front-end app. But we also want our developer(s) to work productively with component properties that make sense to them and within the framework they’re working (i.e. React in our example).

How do we do that?

We could…

…use fields in the CMS that match the components’ properties, though I’ve had little success with this approach. to and children don‘t make much sense to content editors trying to build a button. Believe me, I‘ve tried. I‘ve tried with beginners and experienced editors alike. I‘ve tried helper text. It doesn’t matter. It’s confusing.

What makes more sense is using words editors are more likely to understand, like label or text for children and url for to.

Showing a mockup of CMS text input fields for a button component, one for a to field with https as a placeholder, and another for children with Buy Now as a placeholder.
😕
Showing a mockup of CMS text input  fields for a button component, one for a URL field with https as a placeholder, and another for Button Label with Buy Now as a placeholder.
🤓

But then we’d be out of sync with our code.

Or what if we…

masked attributes in the CMS. Most headless CMS solutions enable you to have a different value for the label of the field than the name that is used when delivering content via an API.

We could label our fields Label and URL, but use children and to as the names. We could. But we probably shouldn’t. Remember what Ian Malcolm said?

On the surface, masking attributes makes sense. It’s a separation of concerns. The editors see something that makes them happy and productive, and the developers work with the names that make sense to them. I like it, but only in theory. In practice, it confuses developers. Debugging a content editor issue often requires digging through extra layers (i.e. time) to find the relationship between labels and field names.

Or why not …

…change the properties. Wouldn’t it be easier for developers to be flexible? They’re the ones designing the system, after all.

Yes, that’s true. But if you follow that rule exclusively, it’s inevitable that you’re going to run into some issue along the way. You’ll likely end up fighting against the framework, or props will just feel goofy.

In our example, using label and url as props for a button works totally fine for data that originates from the CMS. But that also means that any time our developers want to use a button within the code, it looks like this:

<Button label="Go Home" url="/" />

That may seem okay on the surface, but it significantly limits the power of the button. Let’s say I want to support some other feature, like adding an icon within the label. I’m going to need some additional logic or another property for it. If I would have used React’s children approach instead, it would have just worked (likely after some custom styling support).

Okay, so… what do we do?

Introducing transformers

The best approach I’ve found is to separately optimize the editor and developer experiences. Craft a CMS experience that is catered to the editors. Build a codebase that is easy for developers to navigate, understand, and enhance.

The result is that the two experiences will not be in parity with one another. We need some set of utilities to transform the data from the CMS structure into something that can be used by the front-end, regardless of the framework and tooling you’re using.

I call these utilities transformers. (Aren’t I so good at naming things!?) Transformers are responsible for consuming data from your CMS and transforming it into a shape that can be easily consumed by your components.

Illustration showing a rounded square that says Data Source pointing to a yellow box that says transformer, pointing to another rounded square that says component. There is a small code snippet on both sides of the yellow square showing how it transforms the Label and URL fields to Children and To, respectively.

While I‘ve found that transforming data is the smoothest means to get great experiences in both the CMS and the codebase, I don‘t have an obvious solution for how (or perhaps where) those transformations should happen. I‘ve used three different approaches, all of which have their pros and cons. Let’s take a look at them.

1. Alongside components

One approach is to put transformers right alongside the components they are serving. This is the approach I typically take in organizing component-based projects — to keep related files close to one another.

The same illustrated diagram, but the yellow box is now the button index.js file that points to a transformer.js file that then points to a button component.js file.

That means that I often have a directory for every component with a predictable set of files. The index.js acts as the controller for the component. It is responsible for importing and exporting all other relevant files. That makes it trivial to wrap the component with some logic-based behavior. In other words, it could transform properties of the component before rendering it. Here’s what that might look like for our button example:

import React from "react"

import Component from "./component"
import transform from "./transformer"

const Button = props => <Component {...transform(props)} />

export default Button

The transform.js file might look like this:

export default input =&gt; {
  return {
    ...input,
    children: input.children || input.label,
    to: input.to || input.url
  }
}

In this example, if to and children were properties sent to the component, it works just fine! But if label and url were used instead, they are transformed to children and to. That means the <Button> component (component.js) only has to worry about using children and to.

const Button = ({ children, to }) => <a href={to}>{children}</a>

I personally love this approach. It keeps the logic tightly coupled with the component. The biggest downside I‘ve found thus far is that it’s a large number of files and transforms, when the entire dataset for any given page could be transformed earlier in the stack, which would be…

2. At the top of the funnel

The data has to be pulled into the application via some mechanism. Developers use this mechanism to retrieve as much data for the current page or view as possible. Often, the fewer number of queries/requests a page is required to make, the better its performance.

In other words, that mechanism often exists near the top of the funnel (or stack), as opposed to each component pulling its own data in dynamically. (When that’s necessary, I use adapters.)

Another illustration, this time where the yellow box is labeled import mechanism, and it has three arrows, each pointing to a white square, labeled, component 1, component 2, and component 3, respectively.

The mechanism that retrieves the page data could also be responsible for transforming all the data for the given page before it renders any of its components.

In theory, this is a better approach than the first one. It decreases the amount of work the browser has to do, which should improve the front-end performance. That means the server has to do more work, but that’s often a better choice.

In practice, though, this is a lot of work. Data structures can be big, complex, and interwoven. It can take a heck of a lot of work to transform everything into the right format at the top of the funnel, and then pass the transformed data down to components. It’s also more difficult to test because of the potential complexity and variation of the giant data blob retrieved at the top of the stack. With the first approach, testing the transformer logic for the button is trivial. With this approach, you’d want to account for transforming button data anywhere that it might appear in the retrieved data object.

But, if you can pull it off, this is generally the better approach.

3. The middleman engine

The third and final (and magical) approach is to do all this work somewhere else. In this case, we could build an engine (i.e. a small application) that would do the transformations for us, and then make the content available for the application to consume.

Another illustration, this time where the yellow box is the abstracted data source, which points to a white box labeled import mechanism, which then points to the same three white squares representing components that are outlined in the previous illustration.

This is likely even more work than the second approach. And it has added cost and maintenance in running an additional application, which takes more effort to ensure it is rock solid.

The major upside to this approach is that we could build this as an abstracted engine. In other words, any time we bring in data to any front-end application, it goes through this middleman engine. That means if we have two projects that use the same CMS or data source, our work is cut down significantly for the second project.


If you aren‘t doing any of this today and want to start, my advice is to treat these approaches like stepping stones. They grow in complexity and maintenance and power as the application grows. Start with the first approach and see how far that gets you. Then, if you feel like you could benefit from a jump to the second, do it! And if you’re feeling like living dangerously, go for the third!

In the end, what matters most is crafting an experience that both your editors and your developers understand and enjoy. If you can do that, you win!


The post Reconciling Editor Experience and Developer Experience in the CMS appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Components: Server-Side vs. Client-Side

Building a website in 2021? I’m guessing you’re going to take a component-driven approach. It’s all the chatter these days. React and Vue are everywhere (is Angular still a thing?), while other emerging frameworks continue to attempt a push into the spotlight.

Over the last decade or so we’ve seen an explosion of frameworks and tools that help us build sites systematically using components. Early frameworks like AngularJS helped shape the generic concept of web components. Web components are also reusable bits of HTML code that are written in JavaScript and made functional by the browser. They are client-side components.

But components, in a more generic sense, have actually been around much longer. In fact, they go back to the early days of the web. They just haven’t typically been called components, though they still function as such. Server components are also reusable bits of code, but are compiled into HTML before the browser sees them. They are server-side components, and they are still very much a thing today.

Even in a world in which all it seems like we hear is “React, React, React,” both types of components are still relevant and can help us build super awesome websites. Let’s explore how client and server components differ from one another. That will give us a clearer picture of where we came from. And then we’ll have the information we need to dream about the future.

Rendering

Perhaps the biggest difference between client-side and server-side components is what makes them what they are. That is the thing that is responsible for rendering them.

Server components are rendered by — you guessed it! — the server. They aren’t typically referred to as components. They’re often called partials, includes, snippets, or templates, depending on the framework in which they are used.

Server components can take two flavors. The first is the classic approach, which is to render components in real-time based on a request from the client. See here:

Illustrated diagram of a browser making a request to an app server that requests components, which compile to a page and are returned to the app server to go back to the browser.
Server-side rendered components

The second flavor is the Jamstack approach. In this case, the entire site is compiled during a build a process, and static HTML is already available when requested by the client. See here:

An illustrated diagram showing the browser requests a page that has already been created, which consists of components that have gone through a build process from the server, then returns to the browser.
Server components on a Jamstack site have already been compiled into HTML.

In both cases, the client (i.e. your browser) never sees the distinction between your components. It simply receives a bunch of HTML from the server.

Client components, on the other hand, are rendered by — you are two-for-two and on a ROLL! — the client. They are written in JavaScript and rendered by the client (your browser). Because the server is the server and it knows all, it can know about your client components, but whether it cares enough to do anything with them depends on the framework you’re using.

Like server components, there are also two flavors of client components. The first is the more official web component, which makes use of the shadow DOM. The shadow DOM helps with encapsulating styles and other functionality (we’ll talk more about this later). Frameworks like Polymer and Stencil make use of the shadow DOM.

The more popular frameworks, like React and Vue, represent the second flavor of component, which handles DOM manipulation and scoping on their own.

Interactivity

Because server components are just HTML when they are sent to the client, if they are to be interactive on the front end, the application must load JavaScript code separately.

Consider a countdown timer. Its presentation is determined by HTML and CSS (we‘ll come back to the CSS part). But if it is to do its thing (count), it also needs some JavaScript. That means not just bringing in that JavaScript, but also having a means by which the JavaScript can attach itself to the countdown’s HTML element(s), which must either be done manually or with (yet) another framework.

An illustrated diagram showing the browser requesting both a JavaScript bundler containing the JavaScript for the component, and a page with the component's markup that both are returned to the browser.
A component’s HTML and JavaScript are separated in SSR components.

Though this may feel unnecessarily tedious (especially if you’ve been around long enough to have been forced into this approach), there are benefits to it. It is a clear separation of concerns, where server-side code lives in one place, while the functionality lives in another. And it brings only the code it needs for the interactivity (theoretically), which can lessen the burden on the browser.

With client components, the markup and interactivity tend to be tightly coupled, often in the same file or directory. While this can quickly become a mess if you’re not diligent about staying organized, one major benefit to client components is that they already have access to the client. And because they are written in JavaScript, their functionality can ship right alongside their markup (and styles).

A diagram showing the same flow as before, but both the component markup and JavaScript are included in the bundler, which then renders on the page.
Client-side components are all wrapped up in JavaScript code.

Performance

In a one-to-one comparison, server-side components tend to perform better. When the page that a browser receives contains everything it needs for presentation, it’s going to be able to deliver that presentation to the user much quicker.

Diagram showing the browser asking for a page of HTML in a single request.
Technically all you need when rendering SSR components is a single request.

Because client-side components require JavaScript, the browser must download or process additional information (often in separate files) to be able to render the component.

Same diagram as before, but a bundler is requested by the browser along with the page.
Client component often require more code and requests.

That said, client-side components are often used within the context of a larger framework. React has Gatsby and Next, while Vue has Nuxt. These frameworks have mechanisms for creating a superior in-app experience. What I mean is that, while they may be slower to load the first page you visit on a site, they can then focus their energy on delivering subsequent views extremely fast — often faster than a server-side rendered site can deliver its content.

If you’re thinking, Yeah but what about pre-rendering and…

Yes, you’re right. We’ll get there. Also, no more spoilers, please. The rest of us are along for the ride.

Languages

Server components can be written in (almost) any server-side language. This enables you to write your templates in the same language as your application’s logic. For example, applications written with Ruby on Rails use ERB templating by default, which is a form of Ruby. Thus, Rails apps use the same language for the application itself as it does for its components.

The reason client components are written in JavaScript is because that’s the language browsers parse for interactivity on a website. However, JavaScript also has server-based runtimes, the most popular of which is Node.js. That means code for client components could be written in the same language as the application, as long as the application is written with Node (or similar).

Styling (CSS)

When it comes to styling components, server-side components run into the same trouble they face with JavaScript. The styles are typically detached from the components, and require a bit of extra effort to tie styles to the elements on the page.

However, there are frameworks like Tailwind CSS that are working to make this process less painful.

Many client-side component libraries come with CSS support (or at least a pattern for styling) right out of the box. That often means including the styles in the same file as the markup and logic, which can get messy. But typically, with a little effort, you can adjust that approach to your liking.

Welcome to the (hybrid) future

Neither type of component is the answer by itself. Server-side components require additional effort in styling and interactivity that feels unnecessary when we look at the offerings of client components. But then client components have a tendency to take away from performance on the front end. And because the success of a website often depends on user engagement, a lack of performance can hurt the end result and be enough not to want to use client components.

What does that mean for a future that demands both performance and a good developer experience? More than likely, a hybrid approach.

Components are going to have to be rendered on the server side. They just are. That‘s how we optimize performance, and good performance is going to continue to be an attribute of successful websites. But, now that we’ve seen the ease of front-end logic and interactivity using frameworks, again, like React and Vue, those frameworks are here to stay (at least for awhile).

So where are we going?

I think we’re going to see these components come together in three ways in the very near future.

1. Advancement of JavaScript framework frameworks

Remember when you thought up that spoiler about pre-rendering? Well, let’s talk about it now.

Frameworks like Gatsby, Next, and Nuxt act as front-end engines built on top of component frameworks, like React and Vue. They bring together tooling to build a comprehensive front-end experience using their preferred framework. One such feature is pre-rendering, which means these engines will introspect components and then write static HTML on the page while the site is being built. Then, when users view that page, it‘s actually already there. They don’t need JavaScript to view it.

However, JavaScript comes into play through a process called hydration. After the page loads and your user sees all the (static) content, that’s when JavaScript goes to work. It takes over the components to make them interactive. This provides the opportunity to build a client-side, component-based website with some of the benefits of the server, namely performance and SEO.

These tools have gotten super popular because of this approach, and I suspect we’ll see them continue to advance.

2. Baked-in client-side pre-rendering

That’s a lot of compound words.

What I‘ve been thinking about a lot the last couple years is: Why doesn’t React (or Vue) take on server-side rendering? They do, it’s just not super easy to understand or implement without another framework to help.

On one hand, I understand the single-responsibility principle, and that these component frameworks are just ways to build client-side components. But it felt like a huge miss to delegate server-side rendering to bigger, more complex tools like Gatsby and Next (among others).

Well, React has started moving that way. Vue is already there. And Svelte has made this approach a priority from the beginning.

I think we‘re going to see a lot more development while these traditionally client-side-focused tools solve for server-side rendering. I suspect that also means we‘ll hear a little more from Svelte in the future, which seems like it’s ahead of the game in this regard.

That may also lead to the development of more competitors to bulkier tools like Gatsby and Next. For example, look at what Netlify is doing with their website. It‘s an Eleventy project that pulls in Vue components and renders them for use on the server. What it’s missing is the hydration and interactivity piece. I expect that to come together in the very near future.

3. Server-side component interactivity

And still, we can‘t discount the continued use of server-side components. The one side effect of both of the other two advancements is that they’re still using JavaScript frameworks that can feel unnecessary when you only need just a little interactivity.

There must be a simpler way to add just a little JavaScript to make a server-side component that are written in a server-side language more interactive.

Solving that problem seems to be the approach from the folks at Basecamp, who just released Hotwire, which is a means to bring some of the gains of client components to the server, using (almost) any server-side language.

I don‘t know if that means we‘re going to see competition to Hotwire emerge right away. But I do think Hotwire is going to get some attention. And that might just bring folks back to working with full-stack monolithic frameworks like Rails. (Personally, I love that Rails hasn’t become obsolete in this JavaScript-focused world. The more competition we have, the better the web gets.)

Where do you think all this component business is going? Let’s talk about it.


The post Components: Server-Side vs. Client-Side appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Comparing Static Site Generator Build Times

There are so many static site generators (SSGs). It’s overwhelming trying to decide where to start. While an abundance of helpful articles may help wade through the (popular) options, they don’t magically make the decision easy.

I’ve been on a quest to help make that decision easier. A colleague of mine built a static site generator evaluation cheatsheet. It provides a really nice snapshot across numerous popular SSG choices. What’s missing is how they actually perform in action.

One feature every static site generator has in common is that it takes input data, runs it through a templating engine, and outputs HTML files. We typically refer to this process as The Build.

There’s too much nuance, context, and variability needed to compare how various SSGs perform during the build process to display on a spreadsheet — and thus begins our test to benchmark build times against popular static site generators.

This isn’t just to determine which SSG is fastest. Hugo already has that reputation. I mean, they say it on their website — The world’s fastest framework for building websites — so it must be true!

This is an in-depth comparison of build times across multiple popular SSGs and, more importantly, to analyze why those build times look the way they do. Blindly choosing the fastest or discrediting the slowest would be a mistake. Let’s find out why.

The tests

The testing process is designed to start simple — with just a few popular SSGs and a simple data format. A foundation on which to expand to more SSGs and more nuanced data. For today, the test includes six popular SSG choices:

Each test used the following approach and conditions:

  • The data source for each build are Markdown files with a randomly-generated title (as frontmatter) and body (containing three paragraphs of content).
  • The content contains no images.
  • Tests are run in series on a single machine, making the actual values less relevant than the relative comparison among the lot.
  • The output is plain text on an HTML page, run through the default starter, following each SSG’s respective guide on getting started.
  • Each test is a cold run. Caches are cleared and Markdown files are regenerated for every test.

These tests are considered benchmark tests. They are using basic Markdown files and outputting unstyled HTML into the built output.

In other words, the output is technically a website that could be deployed to production, though it’s not really a real-world scenario. Instead, this provides a baseline comparison among these frameworks. The choices you make as a developer using one of these frameworks will adjust the build times in various ways (usually by slowing it down).

For example, one way in which this doesn’t represent the real-world is that we’re testing cold builds. In the real-world, if you have 10,000 Markdown files as your data source and are using Gatsby, you’re going to make use of Gatsby’s cache, which will greatly reduce the build times (by as much as half).

The same can be said for incremental builds, which are related to warm versus cold runs in that they only build the file that changed. We’re not testing the incremental approach in these tests (at this time).

The two tiers of static site generators

Before we do that, let’s first consider that there are really two tiers of static site generators. Let’s call them basic and advanced.

  • Basic generators (which are not basic under the hood) are essentially a command-line interface (CLI) that takes in data and outputs HTML, and can often be extended to process assets (which we’re not doing here).
  • Advanced generators offer something in addition to outputting a static site, such as server-side rendering, serverless functions, and framework integration. They tend to be configured to be more dynamic right out of the box.

I intentionally chose three of each type of generator in this test. Falling into the basic bucket would be Eleventy, Hugo, and Jekyll. The other three are based on a front-end framework and ship with various amounts of tooling. Gatsby and Next are built on React, while Nuxt is built atop Vue.

Basic generatorsAdvanced generators
EleventyGatsby
HugoNext
JekyllNuxt

My hypothesis

Let’s apply the scientific method to this approach because science is fun (and useful)!

My hypothesis is that if an SSG is advanced, then it will perform slower than a basic SSG. I believe the results will reflect that because advanced SSGs have more overhead than basic SSGs. Thus, it’s likely that we’re going to see both groups of generators — basic and advanced — bundled together, in the results with basic generators moving significantly quicker.

Let me expand on that hypothesis a bit.

Linear(ish) and fast

Hugo and Eleventy will fly with smaller datasets. They are (relatively) simple processes in Go and Node.js, respectively, and their build output will reflect that. While both SSG will slow down as the number of files grows, I expect them to remain at the top of the class, though Eleventy may be a little less linear at scale, simply because Go tends to be more performant than Node.

Slow, then fast, but still slow

The advanced, or framework-bound SSGs, will start out and appear slow. I suspect a single-file test to contain a significant difference — milliseconds for the basic ones, compared to several seconds for Gatsby, Next, and Nuxt.

The framework-based SSGs are each built using webpack, bringing a significant amount of overhead along with it, regardless of the amount of content they are processing. That’s the baggage we sign up for in using those tools (more on this later).

But, as we add thousands of files, I suspect we’ll see the gap between the buckets close, though the advanced SSG group will stay farther behind by some significant amount.

In the advanced SSG group, I expect Gatsby to be the fastest, only because it doesn’t have a server-side component to worry about — but that’s just a gut feeling. Next and Nuxt may have optimized this to the point where, if we’re not using that feature, it won’t affect build times. And I suspect Nuxt will beat out Next, only because there is a little less overhead with Vue, compared to React.

Jekyll: The odd child

Ruby is infamously slow. It’s gotten more performant over time, but I don’t expect it to scale with Node, and certainly not with Go. And yet, at the same time, it doesn’t have the baggage of a framework.

At first, I think we’ll see Jekyll as pretty speedy, perhaps even indistinguishable from Eleventy. But as we get to the thousands of files, the performance will take a hit. My gut feeling is that there may exist a point at which Jekyll becomes the slowest of all six. We’ll push up to the 100,000 mark to see for sure.

A hand-drawn line chart showing build time on the y-axis and number of files on the x-asix, where Next is a green line, then nuxt is a yellow line, gatsby is a pink line jekyll is a blue line, eleventy is a teal line and hugo is an orange line. All lines show the build time increasing as the number of files increase, where jekyll has the sharpest slope.

The results are in!

The code that powers these tests are on GitHub. There’s also a site that shows the relative results.

After many iterations of building out a foundation on which these tests could be run, I ended up with a series of 10 runs in three different datasets:

  • Base: A single file, to compare the base build times
  • Small sites: From 1 to 1024 files, doubling each to time (to make it easier to determine if the SSGs scaled linearly)
  • Large sites: From 1,000 to 64,000 files, double on each run. I originally wanted to go up to 128,000 files, but hit some bottlenecks with a few of the frameworks. 64,000 ended up being enough to produce an idea of how the players would scale with even larger sites.

Click or tap the images to view them larger.

Summarizing the results

A few results were surprising to me, while others were expected. Here are the high-level points:

  • As expected, Hugo was the fastest, regardless of size. What I didn’t expect is that it wasn’t even close to any other generator, even at base builds (nor was it linear, but more on that below.)
  • The basic and advanced groups of SSGs are quite obvious when looking at the results for small sites. That was expected, but it was surprising to see Next is faster than Jekyll at 32,000 files, and faster than both Eleventy and Jekyll at 64,000 files. Also surprising is that Jekyll performed faster than Eleventy at 64,000 files.
  • None of the SSGs scale linearly. Next was the closest. Hugo has the appearance of being linear, but only because it’s so much faster than the rest.
  • I figured Gatsby to be the fastest among the advanced frameworks, and suspected it would be the one to get closer to the basics. But Gatsby turned out to be the slowest, producing the most dramatic curve.
  • While it wasn’t specifically mentioned in the hypothesis, the scale of differences was larger than I would have imagined. At one file, Hugo was approximately 170 times faster than Gatsby. But at 64,000 files, it was closer — about 25 times faster. That means that, while Hugo remains the fastest, it actually has the most dramatic exponential growth shape among the lot. It just looks linear because of the scale of the chart.

What does it all mean?

When I shared my results with the creators and maintainers of these SSGs, I generally received the same message. To paraphrase:

The generators that take more time to build do so because they are doing more. They are bringing more to the table for developers to work with, whereas the faster sites (i.e. the “basic” tools) focus their efforts largely in converting templates into HTML files.

I agree.

To sum it up: Scaling Jamstack sites is hard.

The challenges that will present themselves to you, Developer, as you scale a site will vary depending on the site you’re trying to build. That data isn’t captured here because it can’t be — every project is unique in some way.

What it really comes down to is your level of tolerance for waiting in exchange for developer experience.

For example, if you’re going to build a large, image-heavy site with Gatsby, you’re going to pay for it with build times, but you’re also given an immense network of plugins and a foundation on which to build a solid, organized, component-based website. Do the same with Jekyll, and it’s going to take a lot more effort to stay organized and efficient throughout the process, though your builds may run faster.

At work, I typically build sites with Gatsby (or Next, depending on the level of dynamic interactivity required). We’ve worked with the Gatsby framework to build a core on which we can rapidly build highly-customized, image-rich websites, packed with an abundance of components. Our builds become slower as the sites scale, but that’s when we get creative by implementing micro front-ends, offloading image processing, implementing content previews, along with many other optimizations.

On the side, I tend to prefer working with Eleventy. It’s usually just me writing code, and my needs are much simpler. (I like to think of myself as a good client for myself.) I feel I have more control over the output files, which makes it easier for me to get 💯s on client-side performance, and that’s important to me.

In the end, this isn’t only about what is fast or slow. It’s about what works best for you and how long you’re willing to wait.

Wrapping up

This is just the beginning! The goal of this effort was to create a foundation on which we can, together, benchmark relative build times across popular static site generators.

What ideas do you have? What holes can you poke in the process? What can we do to tighten up these tests? How can we make them more like real-world scenarios? Should we offload the processing to a dedicated machine?

These are the questions I’d love for you to help me answer. Let’s talk about it.


The post Comparing Static Site Generator Build Times appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.