Building an optimized Wordpress theme as a SPA
Webpack and Turbolinks to the rescue
For the release of Congo Stories: Battling Five Centuries of Exploitation and Greed, we had the opportunity to partner up with Enough Project. We created a very nice looking website in just a few weeks, built on top of Wordpress.
A quick note about the Wordpress setup
We chose Wordpress for its simplicity of use, “SSR built-in feature” (👋 classic and normal web), and its admin panel… well, no need to present it.
We used Timber with Twig because it was easier to build Twig templates along with the classic ACF so we could customize every single page.
We didn’t use the JSON API to build a full JS app for timeline reasons. I don’t think it would have been the best solution anyway: it’s really just a static website. There are no user forms or live sections… everything is static.
Webpack Setup
We started by updating our tools, using Webpack v.4 and Babel v.7.
Hot Reloading
We wanted to hot reload our application when we were developing. In our case, we had to reload the entire page every time we changed something in JS, but dynamically reload the CSS on runtime.
During development, we created a dynamic/dist/bundle.js
generated by the hot reload server on http://localhost:8080/.
(S)CSS
For the CSS, we used SASS. We had a combination of sass-loader
, postcss-loader
, css-loader
and style-loader
, all allowing hot reloading and source mapping during development.
Development VS Production in Wordpress with Webpack
We needed to load different files or execute different functions depending if we were in development or production mode.
To achieve this, we used the wordpress-debug-webpack-plugin. It allowed us to switch the WP_DEBUG
constant in the whole Wordpress site to true
or false
, depending on if we are in development mode, or not. This is very useful, as we could then have conditionals in our theme to do different actions, depending on the current mode.
Front-end: CSS Critical, Turbolinks, JS/CSS chunks
From the start, we wanted to have different JS chunks depending on the current page you are on. We pushed the concept further, including the CSS needed for each page directly in each JS chunks, so it could be injected when needed.
CSS Critical
Therefore, we created a main SASS entry point that was later compiled/optimized/minified, and injected in all the pages as CSS critical. This main CSS contains the fonts, grid system, main UI, header and footer… Everything that is in common in all of the pages.
It’s not the perfect CSS critical approach, but a relatively simple and smart solution that allows us to inject inline styles without injecting the whole application at once.
Turbolinks
Turbolinks is a lightweight, awesome library that transforms your classic website into a SPA. What it does on a basic level is simple: It captures every click, loads the content of the page you clicked on, and swaps the html of the <body>
for you.
On the JS side, it’s really simple to implement. It triggers events you can listen to, so you can have you own logic on top of it.
Turbolinks caches your HTML internally, so if you use the back button it doesn’t request the page again. It also shows a loader bar when it loads a new page (you can easily override the CSS if needed)
JS/CSS chunks
Before looking at the JS app, we configured Webpack so it could generate chunks:
On the front-end side, a very naïve, yet working way of implementing it is to load the correct chunk based on the Wordpress routing.
Well, not exactly. In our case, once the new HTML was injected in the body thanks to turbolinks, we looked at the id
attribute of the fresh new <div>
injected in our #content
section, and dynamically loaded the chunk “view”.
We had to “manually” specify every single case so Webpack knew how to build the chunks on build time.
Then, in each view, we imported the SCSS we want to inject dynamically:
Easy peasy!
Going to Production
First, we switched WP_DEBUG
back to false
using wordpress-debug-webpack-plugin when we ran the build process.
We also emptied the /static
folder where we wanted to put all the built files used by our Wordpress theme.
As we generated hashed files on build, we needed a manifest file to keep track of the filenames. webpack-assets-manifest generates a manifest.json
for you, so you can retrieve the correct filenames easily.
Then, in our functions.php
we loaded the manifest and transformed it into a php array, so we could access the correct filenames when we rendered the pages.
Back to our previous way of registering JS files, we’ve improved it to take in account our manifest file. We also took this opportunity to defer the scripts/styles, remove the ?ver
added by Wordpress at the end of the path, and inject our main CSS as Critical CSS, using WP_DEBUG
.
Other nuggets…
Not mentioned here, but we’ve implemented a lazyloading using Intersection Observer and its polyfill, used babel preset-env to generate only the needed polyfill (async…), and tested a 32 columns grid with no gutters for the first time. Works like a charm!
Next steps
We’d love to step up our Wordpress game. We’re looking into full static website deployment, and using Cloudinary for the assets so the front-end could request an image depending of the user context (pixel ratio, connectivity, viewport width…)