Moving Backlinko to Headless WordPress and Next.js

Last update: Monday, March 29, 2021

Written by: Thom Krupa

SEO businesses feel the Jamstack vibes. When Google announced their upcoming Page Experience update, Backlinko realized that they needed to improve their website. We are happy we could help.

With Google pushing Core Web Vitals as a ranking factor later this year, more and more businesses acknowledge the growing importance of having a reliable architecture backing website performance.

We've had our share of working with top-notch companies but never before we had the pleasure of working with the company we follow and learn from. So, when Backlinko reached us in May 2020 with the idea of creating Next.js frontend for their website, you can imagine the excitement.

Backlinko Story

Brian Dean, founder of Backlinko and Exploding Topics, started Backlinko in 2013. It has since grown into one of the biggest blogs and brands in the SEO industry. They focus on long, transparent, detailed explanations of SEO topics and trends. If you are into anything digital (marketing, web development, business, etc.), chances are you’ve read something from Backlinko.

We knew they had a big website. However, after the first call and detailed review of the WordPress theme they had, we were amazed at just how much work they’ve put into it. We’re talking about dozens of different landing pages and post templates. Thousands of CSS lines. Lots of legacy JS. Hundreds of components.

Backlinko may look like a simple blog, but once you explore it more in-depth, you realize it’s a massive content machine.

[@portabletext/react] Unknown block type "numbers", specify a component for it in the `components.types` prop
[@portabletext/react] Unknown block type "newsletter", specify a component for it in the `components.types` prop

Performance Issues

Having long posts with several images and user-generated comments had to influence the performance a lot. On top of that, over the years of Backlinko’s WordPress theme development, a lot of legacy code has accumulated that caused page rendering issues, increased Largest Contentful Paint (LCP) and Time to Interactive (TTI) times.

Running a website on a slow, monolithic WordPress install and understanding that optimizing for Core Web Vitals will be the #1 priority for businesses in 2021 prompted the Backlinko team to consider decoupling the stack.

Quote

We struggled with Backlinko's loading speed for years. Due to large, high-res images and illustrations, our page sizes were enormous. And despite optimizing our WordPress theme as much as possible, our load times were still slow.

Brian DeanSEO Expert, Founder of Backlinko.com

Initial contact

It all started with Backlinko’s CTO sending an email asking if we’d be interested in porting Backlinko’s WordPress frontend to Next.js.

We approach each client’s request individually, making sure we can help them achieve expected results. For Backlinko, although not asked for it, we made a proof of concept with a simple demo using various static site generators (with pros and cons of every solution and possible issues) to show how much they can benefit from the Jamstack approach.

Seeing first-hand their content performing much better turned our solid first impression into a helluva pitch. On top of that, the idea of not struggling with the caching strategy, no downtime, and easy scaling, i.e., serving everything from the Edge, opened up a new array of possibilities for them.

So, we quickly decided to work together.

Timeline

We started work in late May 2020. We divided the project into smaller tasks and gradually started moving post templates, pages, and content components. It took us nearly two months to finish everything.

New frontend

From the start, Backlinko was quite convinced to use Next.js, but we weren’t sure if that’s the best option for them. There is no one ultimate or the best tool. With each of our clients, we go through a series of Q&A and research to understand their goals and their needs better so we can find the best tech stack for their use case.

Backlinko is a content-heavy website, and we were afraid of unnecessary big JS payloads. We’ve presented Eleventy as an alternative. At that time, Eleventy was still pretty fresh with no big real-world implementations, mostly personal blogs, and small websites. It was (kind of) risky because the team behind the project is not big and the community is relatively small.

When you’re picking a tech stack, it’s essential to match all business requirements. In this case, hybrid features like preview and out-of-the-box integration with React ecosystem were significant factors. Great community and Vercel company behind Next.js back, plus a lot of case studies and real-world projects made Next.js a safe bet.

Headless WordPress

On top of the complex WordPress setup that they have had in place, we kept WordPress as the CMS because the Backlinko team was happy and well versed with the content editing possibilities it offers.

To separate CMS functionality from the presentation layer, we used WPGraphQL, a free, open-source WordPress plugin that provides an extendable GraphQL schema and API for any WordPress site.

I’ve been following WordPress Rest API development from the very beginning and let me tell you, it has come a long way. After a few years of development, it’s a stable version and ready for any production use. WPGraphQL plugin improved WP headless capability even more.

[@portabletext/react] Unknown block type "lazyVideo", specify a component for it in the `components.types` prop

As you can see in the example above, it’s easy to fetch the data you need and just render them without making a ton of PHP wp_queries. It works excellently with Advanced Custom Fields (ACF) as well, which was the main way how the content was structured in the CMS in this case.

It’s worth mentioning that Backlinko used ACF for years. During the migration to Next.js, we didn’t have to write a lot of custom code or change the ACF structure. We connected the GraphQL plugin, and in most cases, it worked right out of the box.

Static Challenges

All posts and pages are static. Next.js automatically exports every page to static HTML. Instead of fetching data from API on each request, we fetch it just once per build. This, however, led to an increase in build time. We described these issues and fixes on Netlify’s blog.

Dynamic comments area

Besides excellent content, Backlinko is also known for the open discussions they have on each post they publish with a lively and engaging community. The blog pages have more than 20 000 comments.

There are a couple of ways to handle comments in Jamstack. The most common approach is to trigger a rebuild after every new comment submission. But that just doesn’t feel right, plus it is a waste of CI/CD power.

Instead, we decided to pre-built the first couple of dozens of comments. Then we revalidate it using an excellent SWR library (a React Hooks library for remote data fetching) and fetch more comments on the scroll.

It works like this:

  • A reader visits the post and scrolls to the comments section.
  • First comments are available right away since they are pre-build.
  • swr revalidates comments to check if data is not stale. Fetches next batch of comments.

New comments would appear without the need for full reloading of the page. swr handles revalidation on focus and on an interval. If the blog admin approves a new comment, it will be automatically synced with every visitor.

Show, don't tell

With the upcoming Page Experience update tackling poor performance and UX was the main reason behind Backlinkos’ move to the Jamstack. The stack we introduce them to did a pretty good job at improving performance without sacrificing functionality. Take a look at the current Core Web Vitals scores fetched from Google API as you read this post.

By the way, you can test your website in the widget. Just type an URL.

[@portabletext/react] Unknown block type "coreWebVitals", specify a component for it in the `components.types` prop

Not bad, right?

But how does this compare to previous results?

We deployed the new website in July 2020. Thanks to the Treo Site Speed tool, we can check data from the last 12 months and even compare them with their direct competitors:

As you can see, there is a significant decrease in Largest Contentful Paint (LCP), Cumulative Layout Shift (CLS), and Time to First Byte (TFB), and First Input Delay (FID).

Loading time

Here is a direct comparison of loading the new Next.js and the old WordPress website:

[@portabletext/react] Unknown block type "lazyVideo", specify a component for it in the `components.types` prop

Both websites are tested on a 3G connection, Chrome browser, and emulated low-budget Moto G4 mobile device via WebPageTest.

The new website loads three times faster. That’s an outstanding achievement. It’s not just the homepage improvement. One-page optimization is not enough to pass Core Web Vitals. Every single page needs to be fast to have that green badge for an origin.

Quote

We decided to work with Bejamas to help move us over to Next.js. The move made a tremendous difference in our load times and Core Web Vitals scores.

Brian DeanSEO Expert, Founder of Backlinko.com

Road to better User Experience

If the server response is slow, everything else will be slow as well. We improved Time to First Byte, which has an impact on all other metrics. At this point, the loading process is like a waterfall, and you need to optimize every single aspect of it.

Using Netlify Edge gave us an instant advantage. Every file (even HTML!) is served directly from CDN. You can read in the already mentioned Netlify blog post in detail how we seamlessly managed to deploy Next.js on Netlify.

You can achieve it with a standard server and proper cache strategy too. But, as we described earlier, caching is hard, and doing it on a large scale makes it even harder. Netlify guarantees caching invalidation, and every build is globally fast by default with no stale content ever. Yes, you need to wait for the build to finish; that’s the trade-off.

[@portabletext/react] Unknown block type "vitalsImage", specify a component for it in the `components.types` prop

Largest Contentful Paint

To improve LCP, we needed to optimize page rendering. Especially fonts and images. Backlinko uses a lot of different font weights. Instead of loading eight files, we load just one variable font. Crucial was also splitting CSS into smaller chunks. Initially, the WordPress theme was requesting over 100kb of gzipped, blocking CSS.

BTW we plan to further optimize images by adding support for modern image formats like WebP and AVIF.

Cumulative Layout Shift

CLS is a very annoying issue from a user perspective. Website is loading, first elements are visible and then content unexpectedly jumps. It can be caused by a bad fonts loading strategy or conditional rendering on the client-side (like banners or ads). We fixed all things that could cause such jumps.

First Input Delay

React hydration comes with a performance cost. It can significantly increase Time to Interactive (TTI) and FID. The website looks ready before JS execution has been completed. Interactions like client-side routing won’t work without the hydration part. What is the problem exactly?

If you have a ton of JS, the execution takes time, especially on low-end devices. There are some ways to solve this, and the easiest one is to remove runtime JS. Andy Bell did this on his website with a great result.

Often developers argue if that JS cost is worth it. After all, we serve mostly static content. Why do we need to include so much JS? We bet on the ecosystem and the people and tools involved in it. Next.js enables a hybrid approach. We can easily set an on-request preview environment.

Despite React.js hydration being a part of the Next.js framework, FID stays the same.

Perceived Performance

You can’t easily measure perceived performance. It’s very subjective. Often more important is how users perceive performance, not lab tests.

Take a look at the Content Hub. Navigation between pages feels instant. It’s because we preload assets, content and use client-side routing. All these features are built-in in Next.js.

[@portabletext/react] Unknown block type "lazyVideo", specify a component for it in the `components.types` prop

Final thoughts

Successfully balancing performance and functionality is like walking a tightrope, especially with websites like Backlinko.

Still, some tech solutions are better at it than others.

The biggest surprise for me was that we didn’t need to change a lot in the data structure from WordPress, thanks to Advanced Custom Fields and the power of the WPGraphQL plugin. These two make WordPress a really solid and flexible headless CMS.

And the fast Next.js frontend brings a new life to the favorite CMS of many content editors.

While the goal to radically improve the UX and speed of the website is met, the most gratifying result for us was the amazement we got from the Backlinko team when they realize there are no trade-offs between performance, functionality, and security when you take the Jamstack road.

BTW

If you want to go a bit in depth with Next.js and Netlify integration head over to Netlify blog post.

Have a project at hand that’s perfect for the Jamstack approach? Or do you need a better-performing and more secure website? Let’s get in touch!

CLICK HERE to schedule a 1-on-1 talk and learn more about what we can do for you and your business.