Building an optimized Wordpress theme as a SPA

Webpack and Turbolinks to the rescue

Arnaud Tanielian
Team Stink
4 min readDec 5, 2018

--

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…)

--

--

Arnaud Tanielian
Team Stink

Also known as Danetag on the Internets. Engineering Manager @ Shopify