Should You Move from WordPress to a Headless CMS?
WordPress is a great blogging and website creation platform, with a very rich plugin ecosystem.
Astonishingly, about 75 million websites are powered by WordPress. I use and still recommend it for quickly spinning up websites that have simple, standalone content maintenance needs. My own blog that you’re reading here (stevemurch.com) is powered by WordPress, as are other several other sites I’ve created over the years.
But there’s a new hotness in tech, as there always is. Recently, so-called “headless” Content Management Systems — “Headless CMS” for short — have begun to gain adoption, particularly in small and large organizations.
What Is a Headless CMS?
The term is used to describe those systems which provide all the features you’d want in a content-management system, all exposed through an Application Programming Interface (API.) What kinds of features? Well, storing text, video, images, audio, etc., doing searches on that data, publishing it vs. putting it in “draft” mode, etc.
Headless CMS’s provide a generalized administrative dashboard to enter and edit content, manage user permissions and such, but they do not, out-of-the-box, provide any way to display the content, other than to fetch the data via computer code.
Personally, I think “Headless CMS” is a bit of a misnomer, because they do actually have a “head” in computer jargon — i.e., a user-interface. It’s just that it’s a user interface solely for editors and administrators. At first, when I heard the term, I thought “Seriously? you have to write code to upload content, edit and delete articles, etc? No thanks, I’ve already got a back-end document database.”
But in fact, a UX is precisely one of the biggest values of a Headless CMS — the robust UX for data creation and editing, assigning permissions, etc. Headless CMS’s also of course provide storage and delivery of data, just no user interface for end-user consumers of content, by design. They take the philosophy that you already know what you want to do with your data and how you want to display it, and that you have coders who can easily write code to fetch and render.
Here’s Strapi’s UX, as of version 3.4.3:
You can define entities — what Strapi calls “Collection Types.” You might have an Article collection type, an Author collection type, a FrequentQuestion collection type, etc. Once you define the schema of that collection type, you can then add and edit the data. Each of these collection types can have various permissions, so you can separate ownership of particular types of data.
But unlike WordPress, Strapi and other Headless CMS’s can be thought of as “API-first” in some ways. That is, for fetching all this content (and also the administration of it for authenticated users), Strapi exposes a series of well thought out application programming interfaces (API’s) and code libraries, which developers use to plug in to their own website and/or mobile app’s display.
The basic set of API’s include everything you’d expect — getting lists of data, pagination, updating, sorting, filtering, deleting, etc. And they offer a pretty good set of starting libraries for coders, though in my case, I decided just to build out Angular services from scratch, using the standard HttpClient library.
There are a few reasons for the rising popularity of Headless CMS’s:
- Headless CMS’s are easier to integrate with and maintain consistency with parent site’s branding. In the WordPress world, every time a style change is made to the main website, you have to update the theme template for the associated blog. You’re forever locked into making theme/style changes on both platforms, or watching them drift apart over time.
- While it’s not guaranteed, Headless CMS’s can empower better Search Engine Optimization strategies, by more easily letting you move from, say blog.domain.com to domain.com/blog. There’s a fair amount of discussion on this topic, but since Google essentially treats subdomains as entirely separate sites, it’s better to move your content to live under your main domain’s parent if you write primarily about one topic. I will know more about this topic in a few months, as we watch the take-up rate on HipHip’s Video Maker Blog.
- Headless CMS’s allow for clean separation of concerns between development teams and marketing teams
- More specifically, Headless CMS’s allow you to generalize the idea of dynamic content on your website and/or mobile app. In HipHip’s case, we have text (e.g., frequently asked questions), video templates, new video themes, background images, audio and much more that we’d like to release over time. And I’d rather not involve developers and a new production build/push to update these.
- A Create, Read, Update and Delete (CRUD) dashboard is available out-of-the-box, saving development time every time a new asset type is defined.
- Headless CMS’s are generally faster, given the same CPU
- Headless CMS’s are API-first, and have generally well-thought through REST endpoints. There’s no kludgy integration between content systems and platforms.
HipHip’s Migration Story
About two weeks ago, for all of the reasons above, I decided that I wanted to move our nascent blog from blog.hiphip.app (which was powered by WordPress, hosted on Amazon Lightsail) to https://hiphip.app/blog.
There are lots of headless CMS choices in the market, but as head of a small scrappy startup, I decided to go with the open source approach, and landed on Strapi as the choice. Its Community Edition is free.
Strapi is a Node application, which can be deployed on Linux or Windows. So, I spun up a Linux virtual machine on Azure, and deployed it. Strapi has clear instructions on Azure deployment here. I found them pretty accurate and complete.
- Azure setup
- Network configuration
- Defining collection types
- Migrating content
- Setting up user permissions
- Testing the API
- Building front-end
- Replicating some plugin functionality: (Related Posts, AMP pages)
Network Config and Subdomain
Because I wanted my code and dashboard to have its own subdomain name, I then configured the Azure Application Gateway which is in charge of all inbound traffic to the hiphip.app domain. I pointed the subdomain to my Strapi instance, and set some permissions and firewalls and ports. I gave my Strapi instance its own special host name in my domain registrar, and pointed it to the Azure Application Gateway. That Application Gateway, in turn, needed a “Listener” for the inbound subdomain and direction to the “backend pool” of the Strapi application and port. Read more about Host Listeners and Rules in Azure Application Gateway.
I also installed the Azure Storage Blob plugin to Strapi, which works like a charm. The result is that all our media assets are uploaded to Azure Storage Containers.
Migrating Content from WordPress to Strapi
As for the actual migration of content, you’re kind of on your own at present writing. There’s good tech article here explaining how you can use axios to fetch content from the WordPress “/posts” feed and POST it into Strapi. That can work well.
For Media, I installed the WordPress Media Download plugin on my old WordPress-powered site, then got a ZIP file of all the old site’s media library. I unzipped that file and literally dragged and dropped all the JPGs and media onto the Strapi Media Upload box. After about five minutes, all the media at least from the old posts was now in the new Strapi instance.
But as for the article text itself, I chose not to go the programmatic route, because we only had 22 blog posts, and just copying and pasting the posts one-by-one into Strapi’s editor was more time-efficient. That’s because there is still some work converting WordPress HTML to Strapi’s preferred Markup format.
Of course, the “headless” in Headless CMS means you don’t get any out-of-the-box views to render out as your blog. But that work was pretty quick to integrate into HipHip.app.
Our front-end website is an Angular app which uses the excellent Tailwind CSS framework. I have all the master views, navbars, styling and branding ready to go. Writing a simple “fetch this from an API and display it” is maybe a thirty minute job.
I found a great technote from Mark Rabey to create a Markdown pipe in Angular. If you’re not familiar with Angular, “pipes” are display transformers when views are rendered. Thanks to Mark’s approach, all I have to write in the code to transform Markdown to HTML is something like:
<div [innerHTML]="markdownString | marked"></div>
The result? A new blog at https://hiphip.app/blog. It’s fast, and I can change the look and feel over time. The HipHip Video Maker Blog can be found here.
Replicating Some Plugins
The first plugin I wanted to replicate was the “Related Posts” plugin, which helps viewers see related content on a blog-post detail page. In HipHip’s case, our simple blog post format includes a “category”, and I wanted to show the most recent three posts in that category.
Now, I could have simply this rendered dynamically on each page load.
But at this same time, I wanted to deliver major page-render speed enhancements and SEO benefits, so I’ve implemented Scully, the static site generator for Angular into this project. I’ll probably be doing another post on Scully, but essentially, Scully is a post-build step which pre-renders the major pages of your site. Clearly, for a blog, which is mostly static content anyway, a tool like Scully is perfect, so these pages can be SEO-optimized and render blazingly fast.
This relates to the “Related Posts” idea because I didn’t want to render these at display time. This is where Strapi’s great webhooks feature comes in. You can assign webhooks to any collection type in Strapi which get POSTed to when the content changes.
So, I simply defined a webhook on the blogposts collection to call-out to our main production server, saying effectively “Hey HipHip, there’s a new blog post, or an update to one. Please go and re-build the related-posts links.” And that’s precisely what happens. Since blog post creation is a relatively infrequent operation, this allows all of these to be cached for display.
Accelerated Mobile Pages (AMP) deliver ultimate speed for mobile users, and recently have earned pretty favored status on Google. They have huge restrictions on what can be used (the answer, generally is “it cannot.”) This keeps AMP pages really fast, and cacheable in a distributed content delivery network.
With WordPress, you can simply install one of many plugins to deliver AMP-friendly versions of your website.
But with Strapi, or pretty much any headless CMS, you’re kind of on-your-own. The approach I took was to write a Python script to build AMP pages for each of our blog post pages before test/deploy time.
First Impressions of Strapi
Overall, I’m pleased, and particularly excited about the potential to make formerly “hard-coded” aspects of HipHip more dynamic.
For instance, one new type of content I’d like to do is offer a list of custom ice-breaker suggestions for every occasion as to what “card signers” might want to write or say. These are occasion-specific — i.e., what you might want to suggest as an ice-breaker for a wedding will not be the same as a graduation or birthday or get-well wishes, etc.
Another use-case: HipHip has also just gone to beta with a fun virtual karaoke feature, letting you sing Happy Birthday! to a special someone in a virtual chorus of friends. I’ll be adding more songs over time, and it’d be great not to hard-code this into the platform. Soon, we should be able to go into the Strapi CMS, add a new video and introductory text, and publish it, and voila! It’ll be live.
The adoption of a Headless CMS is also allowing me to think more broadly organizationally about scaling out, by hiring some remote freelance writers to contribute to the project.
HipHip has been up and running with Strapi for only four days so far, so we haven’t put it through all its paces, and are just starting to find opportunities to make our lives easier with it. But gone are the days where text changes on the website will require new production builds.
Using a headless CMS makes it so much easier to turn the text and content part of your website into dynamic strings. So we might even do localized versions of the website, to allow it to be used in different markets.
Strapi is relatively new (it’s in version 3.4.x at this writing.) The biggest areas for improvement that I can see as a new user are in the following areas:
- Markdown editor — The existing Markdown editor in the Strapi instance is adequate. But there are some major annoyances in the way it handles bullet points, numbers, etc., at least in the Chrome and Edge browser. Those of us who are used to the de-facto standard of just being able to press “ctrl-k” to insert a link have to re-learn a two or three click dance to do the same. Related, the tools that do support Markdown don’t have easy “export to Strapi” publishers. I’d actually like to see some kind of dedicated download client that let you create and edit content then “push” to the server.
- Better UX for Remote Work Management: In particular, the job of farming out articles to freelancers involves a lot of teaching/asking them “Please supply this in Markdown format, with any JPGs in a subdirectory, zipped up, etc.” format. Perhaps Strapi could consider some kind of out-of-the-box, robust Markdown editor feature-set for a remote workforce. Right now, the Strapi dashboard is a little too wonky to send to, say, an ad-hoc editor or writer. I could see that for a dedicated team, a couple hours of training and usage would be no problem.
- No way to inline paste-in images from the clipboard — For blogs in particular, I’m frequently wanting to Paste an image from the clipboard. WordPress’s Gutenburg editor makes this simple. Just ctrl-v (or command-v on Mac) and you’re done. No-can-do on Strapi; you’ve got to do the “File > Save”, “Upload… > Choose File…” dance.
- Restart required for any data type changes — Oddly, Strapi feels it has to restart every single time you introduce a new collection or change a data type. The result is that you cannot easily, say, run one instance on your local development machine and one in production and keep them in sync. In fact, you can have sheer panic for a moment if suddenly data types disappear on you.
- Permissions and Default Users — The existing permissions engine, though robust, can be frustrating for first-time use. They could do a lot of work here to pre-configure an API user with the right permissions for read access, suggest another one for administrative access (but perhaps not activate it out of the box.) Related, their error messages need to be much better. It’s very common to run into “400 Bad Request” and “403 Forbidden” errors, and not realize that by default, certain users don’t have privileges, even if by all rights, you think they should.
- No tools to migrate from WordPress — As written above, you’re kind of on your own right now with programmatic WordPress to Strapi content migration. If I were Strapi’s CEO flush with fresh $4 million funding from Accel, I’d absolutely be paying developer(s) to build out a robust WordPress migration tool.
Should You Move?
Here’s my advice. If you have a personal blog, such as the one you’re reading here, I see no reason to migrate to a Headless CMS setup. WordPress is venerable, reliable and extremely flexible.
If however you have more than one person in your organization, particularly with a dedicated development team and marketing team, you should seriously consider Headless CMS adoption. If you have a company or organization, from small to large, which may or may not have a blog, but which is large enough to have a development team, there are major reasons I’d strongly consider making a Headless CMS a key part of your platform stack.
Think about all the times you’ve had to push “content” (i.e., non-software platform) changes through developers. They shouldn’t be writing that copy or implementing HTML display of that copy, especially if it is likely to change a lot over time (e.g., seasonally.)
By using a Headless CMS, you’ll separate the marketers and media content editors from the developer and operations teams’ workflows, which is a very good thing. You’ll eliminate all that development time and hassle involved with building out the forms-based user interface to enter, edit and manage images, text, video, and audio. You’ll allow your marketing team to think more broadly about what kinds of content they’d like to add to your brand over time.
By letting your developers get out of the way of blocking content creation and editing, it’ll open your business up to a world of media possibilities, and likely make each team happier.