Getting Away from PHP

Goodbye WordPress, hello Next.js
November 22nd, 20258.2 mins

For over a decade, I've been an outspoken supporter of WordPress. I've always advocated open-source software, and WordPress was in no small part responsible for my first break in the tech industry. In that time, I've released dozens of WordPress plugins, worked for the Easy Digital Downloads team, contributed to WordPress core, and spoken at and organized WordCamps. For many years, I've said that despite its humble beginnings as a fork of the b2/cafelog, WordPress is capable of anything. WordPress is almost infinitely extensible, and to this day, I stand by that. So what's wrong with WordPress?

The Problems with WordPress

Unfortunately, its greatest strength is also one of its most notable weaknesses. The extensibility of WordPress means an almost limitless number of plugins are available, and there is little standardization or oversight to the ecosystem. Every plugin looks and behaves differently, and few even have basic safety checks on variables or input sanitization. As a result, the WordPress dashboard can quickly become an unmanageable behemoth that requires heavy customization to be usable by a non-technical client.

This ecosystem also creates another problem simply by its existence. Since WordPress makes it easy to find and install plugins and update both plugins and itself, it's all too common for clients to randomly install or update things without thinking about, let alone understanding, the impact those decisions can have on the stability of their website. More than once, I've had a client install a random plugin from the WordPress repo and end up with a broken site. Inevitably, this leads to me getting a panicked call and protestations that they didn't do anything that could have caused the mission-critical breakage, and every second they are down is costing them money. Forget that the system logged them installing a random plugin after I told them not to; it's clearly my fault. Similarly, the dashboard showing a user-friendly notice that a WordPress update is available doesn't necessarily mean that every plugin you are using is fully compatible with that update. Staging sites exist for a reason, but users seem perfectly happy to destroy their site monthly and blame the developer. It gets old. Fast.

But for the sake of argument, let's pretend that every plugin in use on a particular WordPress site was custom-written for that site by the development team responsible for it. And let's pretend that the client doesn't randomly install plugins or click buttons they shouldn't. In that fairy tale scenario, a WordPress site should run smoothly, right? Maybe. There are still a few things holding WordPress back.

WordPress is a 22-year-old platform written predominantly in PHP. Since day one, it has maintained a relatively smooth update path. As a result, there is a reasonable amount of extra code to handle legacy standards. Additionally, as a fork of b2, WordPress is written in PHP. While PHP is still a widely used language, it requires more resources to keep a website running smoothly than many modern alternatives.

Finally, keeping a WordPress site secure is nearly a full-time job by itself. The lack of standardization in safety checks means that every plugin installed increases the number of ways a hacker can potentially gain access to the site. Additionally, there are potential vulnerabilities in WordPress core, as well as the hosting platform and the server itself. In the case of most business sites, you have to contend with SLAs, which mean consistent maintenance is necessary to prevent potential downtime and lost revenue.

The cumulative effect of these issues is that maintaining WordPress sites quickly becomes more trouble than it's worth, especially when you're the sole developer, designer, and server administrator on the project. Despite this, I've been maintaining dozens of sites for the last decade. I've had enough.

Moving Away from WordPress

Over the last few years, I've been increasingly needing JavaScript for various projects. I've worked on several websites written in Vue or Next.js, as well as desktop applications in Electron and React Native. I've grown to rather like working on them, but until now, I hadn't built a solo full-scale site in JavaScript. I have a few in-progress solo projects in React and React Native, but none that are remotely close to complete. I knew I wanted to move my website off WordPress, so I started researching the pros and cons of various frameworks.

Because nobody but me needs to edit this site, I saw no reason to build a traditional dashboard. Skipping the dashboard meant one less potential security issue and a smaller footprint. Conversely, I didn't want to hard-code every blog post; that wouldn't be overly manageable in the long term. Markdown seemed like the ideal solution. Frameworks like Gatsby and Jekyll allow writing content in Markdown, but I didn't particularly want to lock myself into another framework; I prefer to understand everything under the hood with my website, and learning a new framework for just one website seemed inefficient. Additionally, I found the theming systems for both systems to be a headache. I didn't like building themes in WordPress, and wanted more direct control over how the design worked than I'd had in the past.

I also needed to ensure I could maintain a variety of content types. Beyond the basics of pages and posts, I have several post varieties in my archive, and I wanted to be able to migrate directly from WordPress with as few functional changes as possible. After a fair bit of research, I decided that Next.js checked all of my proverbial boxes. Particularly with their most recent major release, dynamic routing makes building a blog-like structure trivially simple, and their official support for MDX meant I could write my content in Markdown and easily import it where it needed to be on the site. Additionally, if I ever lost my server, the content being available in Markdown format meant the content itself would survive.

Rebuilding in Next.js

Having chosen to rebuild with Next.js, I built a blank framework with little more than the site header and footer. I knew I wanted to maintain the basic look of the WordPress site, so the design aspect was simple; I just had to figure out how to convert that design. After experimenting with several component libraries, including Chakra UI and Mantine, I decided to skip the overhead of a component library and build my own components. This decision led me to Tailwind CSS, a utility-first CSS framework that doesn't try to force you into any particular design philosophy. Using Tailwind, I had no trouble making the header and footer resemble the old WordPress site, and quickly moved on to figuring out content.

I started by converting my three most recent posts to Markdown and working out the basics needed to import MDX content. This process began as little more than printing a few text links on the homepage and some raw content on the individual post pages. Over the next day, I added a hero component, set up the content area, and reworked the footer so it displayed on all pages. I also spent some time debating how to handle those articles that included reviews. The review system on the WordPress site was quite flexible, and this one needed to match its functionality. In the end, I settled on using MDX for reviews as well, and it took the better part of a day to work out how to load and display them.

Over the next few days, I worked on migrating all of my blog posts to Markdown. For most posts, this just meant copying the page content, reformatting it for Markdown, and migrating metadata to the new format. Every once in a while, I'd come across a specific feature that I used on WordPress I hadn't accounted for yet on the Next.js site. Working through the content in this way meant that as edge cases appeared, I developed the corresponding functionality, limiting possible omissions.

By day ten in the process, I had migrated all the post content and moved on to pages. After a week of working through posts, I finished the pages comparatively quickly, and it was time to start working on the homepage design. Once the homepage was complete, I spent a few days improving the codebase before finally putting the new site online. It was a total of sixteen days from initial commit to publishing the site. By the end of the process, the Next.js and WordPress sites were so similar that I more than once forgot which site I was looking at. Mission accomplished!

Plans for the Future

Having completed the initial migration doesn't mean the project is complete. I'm still early in the learning process when it comes to developing with Next.js. There are things I know I can do better, and things I still want to try in the future. While I have some automated testing on the new site, it's not up to the standards I set for myself when I was building with PHP. For example, I'm not currently testing MDX files. The linting I've done in the past for Markdown doesn't necessarily work well with MDX, and I haven't found a way to implement it properly. I also have some new features I intend to add; things I've wanted to do for a while, but felt limited by what was available in WordPress. I have a lot to do, but I'm enjoying the process and don't feel like I'm burning out for once; hopefully, I'll make good progress fairly quickly. If nothing else, I'm posting again!