Why I migrated my website from Next.js to Eleventy
March 27, 2024
Here's why I switched from Next.js to Eleventy, opting for a lightweight static site approach for my blog site. I describe the benefits, deployment strategies, and the performance results in this blog post.
I'm very familiar with Next.js. I've been using it for several years and have built several websites with it. I have watched it evolve and mature into a powerful React framework.
And that's why I built my own personal website with Next.js.
I'm not writing this article to bash Next.js. It is a powerful React framework. I fully intend to use it to build more sites in future.
But for a small site like this one, it's complete overkill. I needed something lighter.
Enter: Eleventy.
Eleventy
If you haven't heard of Eleventy before, it's what is known as a Static Site Generator (SSG) and it's written in Node.js.
As the name suggests, it generates static sites. This is in contrast to a framework like Next.js which generates something more like a web application - a hybrid of server-side code and a bundle of JavaScript shipped to the client.
A static site generator simply outputs a bunch of static assets - HTML, CSS and, should you choose, JavaScript. These can be served by any old web server - or even something like GitHub pages.
For my site, this is perfect. I have some blog posts and a few static pages. I don't need to manage state (e.g. logins), I don't have need for lots of interactive components, and I don't have lots of content changing all the time.
Instead, I want the site to be fast and lightweight. And that's what I achieved with Eleventy.
Deploying
In total, I spent a few hours here and there over the course of a couple of weeks working on the site rebuild. I made some trade-off decisions to get it a first version launched nice and quickly, but I'm really happy with the results.
Before I talk about the site itself, I think it warrants mentioning the deployment approach, as IMHO this is where some of the coolest stuff happens.
Hosting
Let's start with hosting. This site is running directly from the server (a used Dell OptiPlex 7040 SFF PC I bought on eBay for $85) in my shared office.
Although the office has a symmetric gigabit fiber WAN connection, it doesn't have a static external IP, nor would I be able to open ports and forward the traffic anyway.
This probably deserves its own blog post - I know a great place to post them 😉 - but the TL;DR is that I have a TCP reverse proxy on a Hetzner VPS sending the traffic over a Wireguard VPN connection to my server where TLS termination occurs and the requests are handled.
It's a bit of a proof-of-concept for me, but so far it's working amazingly well.
Docker image
I'm a big fan of using Docker for deploying websites, and I decided to stick with that for this site too. Since Eleventy just spits out static assets, I just needed a simple web server to serve them. The obvious choice to me was nginx, which excels at such tasks and I'm familiar with already, so I was quite surprised to find very little information out there on deploying Eleventy-based sites with nginx.
Nonetheless, that's what I chose. I'll definitely be doing a blog post about that, as I think it might really help others out there.
As one of my let's-just-get-this-live decisions, I am updating my site my simply tarballing the Docker image, scp
-ing it to my server, and then recreating the container.
Given my home internet setup is presently a not-very-performant cellular connection, I wanted to make this image as small as possible. I chose to go with a distroless Docker image containing a trimmed-down version of nginx. All told, the entire Docker image complete with all the assets (including pre-compressed files) weighs in at <10MB, and barely 4MB when tarballed and gzipped! Not bad considering nginx:slim
weighs in at a healthy 47MB.
Results
So what's the end result?
Performance
Well, to say the site is fast would be an understatement.
Loading the home page requires just one single request. That lone request is for the HTML page itself, weighing in at a meagre 4KiB after brotli compression. And that includes all the CSS & JS (more on that in a minute) the page needs to render.
In fact, it's so fast that it pixellates the timeline view in WebPageTest! It takes under 250ms to completely fetch and render the entire page. Again, that's from my office-server - not from a CDN edge cache!
And other pages on the site are just as lightweight - blog posts coming in at somewhere around 10KiB, with images being the only additional HTTP round-trips required.
UX & Design
OK, I'm no designer. I like to think my site is clean and minimalist, but that's as much out of necessity as it is out of choice. But I have tried to make it usable and accessible.
I've attempted to make the HTML as semantically correct as possible. It uses the appropriate HTML elements where applicable, and ARIA attributes where relevant.
The design is mobile-first with progressive enhancement, meaning all media-queries build on the mobile rules so lower power devices have less CSS to parse.
JavaScript is used to make the interactive menu for mobile screen sizes, but even this is optional. Visitors without JavaScript will find the menu permanently open.
I've also made it respond to both a reduced motion preference and dark mode preference - personally I find dark mode strains my eyes more than light mode in most situations, but I respect that a lot of people prefer dark mode. While the site doesn't have a preference toggle (yet), it does at least respect the OS / browser preference.
Next Steps
At the moment, the site is very barebones. Now it's live, I'm going to keep iterating on the design, tweaking and refining it while trying to stay true to its original goal of being clean, simple and fast.
Additionally, there are some technical features I'd like to add, so stay tuned!
And I do want to write up some deep-dive blog posts on some of the more nuanced elements of this site build - the distroless nginx Docker container, the proxy / VPN solution for self-hosting my site, and a couple of other items.
Summary
It's my first time working with Eleventy and it's been a lot of fun. As with anything, there's a learning curve, but there are a lot of great blog posts out there and the community on Mastodon & Discord is incredibly responsive too.
While I may have lost the power of the React framework that underpins Next.js, for a project like this, I've gained so much more by having a lightweight and highly performant website. For future projects, I'm definitely going to be including Eleventy in my consideration set.