The Publishing Project

Customizing WordPress

How do we customize WordPress? How do we make themes our own and add functionality that is not part of a theme or that you want to use regardless of the theme you have installed?

This post will explore the basics of creating custom WordPress elements and will cover plugins, child themes, and custom themes.


The specifics of your situation will be different than mine. You should always analyze your needs and requirements before making a decision.


The first thing to do when planning to add things to a theme is to decide what’s the best alternative to get it done.

Do you use an existing plugin to work with your theme? WordPress has a huge collection of plugins to accomplish a variety of tasks; it may just pay to evaluate existing plugins to see if they work.

Same things with plugins. Evaluate existing plugins and test the existing capabilities and expand from there. Only if you evaluate existing themes and none do what you want without extensive modifications you should consider building a brand new theme using a starter kit like _s or the themes listed in articles like 10 Best WordPress Starter Themes for Developers in 2020 or 19 Best WordPress Starter Themes for Developers in 2020. Test the themes and don’t take anything for granted.


I recommend using plugins when you have a single-purpose task or script that you want to use across multiple themes or sites.

For example, in a previous post I discussed how to add the scripts for Quotebacks to my WordPress installation. I want to make sure that this functionality works on every theme I may want to use so, rather than copying the enqueue function to whatever theme I choose to use, I create a simple plugin to do it once and leave it in the site regardless of the theme

I won’t go into the details of creating a plugin, we could spend entire articles covering just that. Instead, I will refer you to Plugin Basics from the Plugin Developer Handbook and WordPress Essentials: How To Create A WordPress Plugin as good starting points to use when creating your own plugins.


Themes are the look and feel of your WordPress site. They can also bundle functionality that the developer considered essential for the theme to work although the bundling is not required, you can give users a list of plugins to install on their own.

We’ll discuss two ways to work with WordPress themes: Child Themes and Custom themes from scratch.

Child Themes

Child themes are the easiest way to customize a theme without writing them from scratch. Some of the advantages of using child themes:

  • You’re building on something that already exists, thus speeding up development time
  • You can leverage the functionality of sophisticated frameworks and parent themes while customizing the design to your needs
  • Upgrade the parent theme without losing your customizations
  • Since you didn’t modify the parent theme you can disable the child theme and everything will be as it was before
  • It’s a good way to learn

Assuming that you’ve already uploaded the theme that you want to use as a parent; the idea is to create an empty folder and add two required files.

To create a child theme follow these steps:

  1. Create a folder for your theme (in this case we’ll call it Twenty Twenty Rivendellweb)
  2. In the folder create the following files
    • a style.css stylesheet
    • a functions.php file

For now, don’t worry that the files are empty. We’ll populate them as we move forward in the tutorial.

In styles.css add the following comment.

 Theme Name:   Twenty Twenty Rivendellweb
 Theme URI:
 Description:  Twenty Twenty Child Theme
 Author:       Carlos Araya
 Author URI:
 Template:     twentytwenty
 Version:      1.0.0
 License:      GPL2.0 or later
 License URI:
 Tags:         light, dark, two-columns, right-sidebar, responsive-layout, accessibility-ready
 Text Domain:  twentytwentyrivendellweb

You can add further CSS customizations to make the site look the way you want to but it’s not a requirement, the comment is enough.

In the PHP file, we need to add the parent theme’s stylesheet and the style that we just created.

To add the style sheets in a WordPress safe way we create a function, my_theme_enqueue_styles to put the code we want to run.

function my_theme_enqueue_styles() {

  // This is 'twentytwenty-style'
  // for the Twenty Twenty theme.
  $parent_style = 'parent-style';

  wp_enqueue_style( $parent_style,
    get_template_directory_uri() . '/style.css' );
  wp_enqueue_style( 'child-style',
    get_stylesheet_directory_uri() . '/style.css',
    array( $parent_style ),
add_action( 'wp_enqueue_scripts', 'my_theme_enqueue_styles' );

The first call to wp_enqueue_styles will add the parent theme’s stylesheet by using get_template_directory()to build a URI pointing to the parent theme.

The second wp_enqueue_styles will load the child theme’s stylesheet by using get_stylesheet_directory_uri() to retrieve the locaation of the child theme’s stylesheet.

The final step is to call the wp_enqueue_scripts action with the function we created as the callback.

This is the basic child theme.

If we want to modify a template we copy it from the parent theme and modify the copy in the child theme.

Same with new templates. We create them in the child theme and edit them there.

You can find more information on child themes in How To Create And Customize A WordPress Child Theme

The Twenty Twenty child theme I created using these techniques is in a Github Repo

Custom Themes

As good as child themes are there are times when the number of changes makes the child theme strategy too cumbersome.

That’s where we create a brand new theme, either completely from scratch or using a starter theme.

There are plenty of starter themes developers to choose from. 19 Best WordPress Starter Themes for Developers in 2020 lists starter themes for you to consider.

There are themes that are bundled with libraries and designed for specific workflows. These starter themes include:

I selected Underscores (without Bootstrap) for my theme development. It gives me the best combination of ready-to-use features and ease of creating tooling around the elements we create.

Custom themes are way harder to conceptualize what we want to do and how we want to do it. Because of these difficulties, it’s hard to prescribe what to do with custom themes. I would suggest starting with the following questions:

  • What type of site am I designing?
  • Do I need to add any additional templates?
  • How am I planning to modify the existing templates?
  • Who is the audience?
  • What sites am I competing with?
  • Do I have analytics to confirm my audience? Is there a comparable site to evaluate?
  • What browsers do I have to support
    • How does browser support limit the technologies I want to use?
  • How am I handling responsive design?

These are some basic questions, there are others that may become relevant as you build the site.

I learned a lot about Underscores from Morten Rand-Hendriksen’s WordPress: Building Themes from Scratch Using Underscores course on Linkedin Learning. Although the course is from 2017, the code hasn’t changed enough, in my opinion, to not use it.

Do you know where your third parties are going?

Third-party scripts on your site present a potentially dangerous side effect. We don’t know what additional assets third-party load. That has security and performance implications. This post will look at the performance side of the issue.

As Simon Hearne writes in How to Find the Third-Parties on Your Site using third party scripts that, in turn, make calls to additional sites and parties outside their (and your) control can affect the performance of your site. Quoting the post:

I ran the homepage through WebPageTest and sure enough, there were a bunch of calls to various subdomains of Thankfully WebPageTest stores initiator, referer and redirect headers; so with a little work, you can find out where these third-party calls come from. The director was correct, there were no calls to Facebook on their site. It was a third-party creating fourth-party calls to Facebook! This can have serious ramifications if Facebook (or any other third-party call) affects customer experience.

So why does this matter?

As Steve Souders (Frontend SPOF and Frontend SPOF survey), Pat Meenan (Testing for Frontend SPOF), and Joshua Bixby (How vulnerable is your site to third-party failure?) write, “fourth party” scripts (scripts called from third party code) can have adverse effects on your site’s performance. You (and potentially your client) have no way of controlling what these scripts do and whether they will block or slow down rendering.

In Things to Know (and Potential Dangers) with Third-Party Scripts, Yaphi Berhanu list additional concerns about third-party scripts that go beyond performance. The article is from 2017 still presents points that are still worth researching.


A very interesting to see the number of requests your third party scripts generate is to run your site through Request Map and see the results. I was surprised to see how many of its submodules a script requested in the layout-experiments site.

The images that follow present Request Map results for three different websites, two sites I own, and

For the CNN request map, note that the circles are smaller and the name of individual assets is impossible to read because of the number of assets involved.

Requet map for Created from Request Map Generator
request map for
Requet map for Created from Request Map Generator
request map for
Requet map for CNN. Created from Request Map Generator

Solving the issue

In an ideal world, you’d be able to trust your third party scripts that they will do a minimum due diligence on their dependencies.

But in the real world, we don’t have that luxury. We use what we must and we hope that scripts that are loaded by our third-party scripts will not slow down our applications.

Tools like request map will not stop the spread of unknown and unwanted scripts but they will help when clients ask you where did that script comes from and when you go to the third party script providers and ask why are the additional assets being loaded.

Implementing Quotebacks in WordPress

Quotebacks are an interesting way to cite content from other websites. They work in two stages.

Installing the extension and getting the quotations

The first one is HTML formatted with special data attributes, a footer and a link to the script. There are browser extensions to create this code, one for Chrome and one for Firefox (currently under development).

Once the extension is installed, go to a page, select text and press command + shift + S on Mac and control + shift + S on Windows

The result of highlighting text and pressing command/control + shift + S to generate the quote back. There are options to copy embed, copy markdown or close the quote at the bottom of the caption.

The final step in this section is to paste the code into the page that you want to use

Installing the script

The second part, the quoteback.js scripts converts the blockquote into a custom element with its own built-in styles. The installation is simple, either create a script and point the source to the CDN

  src="[email protected]/quoteback.js">

Or download the script and link to it locally

<script src="js/vendor/quoteback.js></script>

Completing both steps will produce results similar to the following image.

Example of a formatted quoteback

Why Use them?

Using these types of citations keep me honest and it makes me think about the type of resources I use and how I use them. It also allows readers to get the full text and context of your citations and form their own opinions about the subject.

WordPress specifics

There are a few WordPress-specific issues that we need to address as we implement them in a WordPress site.

Linking to a CDN or hosting locally?

This is a tricky question.

If we run the plugin from CDN, we get the latest code but we add one or more request to those already being made and, potentially making the site slower since the custom elements will only be upgraded when the script finishes loading.

Keeping a local copy makes the code more efficient, it reduces the number of requests, but it puts the weight of updating the code on the plugin creator.

For this project I chose to host the script as part of the plugin.

Plugin or theme?

Whether to host it in a plugin or a theme will depend on what you want to do with it.

If you want the functionality to be part of a theme and don’t mind if the code breaks when you move to another theme, then a theme would be ok.

But if you want to keep the functionality regardless of the theme you’re using, then you must use a plugin.

For this project I’ve chosen to build it as a plugin.


To create a WordPress plugin we need to add a preamble comment that contains the plugin metadata that WordPress will use to show the plugin in the admin plugin menu.

I also incorporate a basic tool to prevent direct access to the plugin. If the constant ABSPATH is not defined then the user tried to access the script directly and we don’t allow that to happen so we exit.

  Plugin Name: WP-Quotebacks
  Plugin URI:
  description: Backend work for Quotebacks See <a href=""></a>
  Version: 1.0
  Author: Carlos Araya
  Author URI:
  License: MIT

if ( ! defined( 'ABSPATH' ) ) {

The main part of the script is the function and action to add the script to the pages.

We don’t add the script like we’d add it in a regular page, we need to use WordPress-specific functions, enqueue_script, to add them to all pages on the WordPress installation.

By itself enqueue_script will not add the script, for that we need to add an action that uses the enqueue_scripts hook (note the plural) and a function callback.

function quotebacks_enqueue_script() {
    'vendor/quoteback.js', __FILE__ ),

add_action( 'wp_enqueue_scripts', 'quotebacks_enqueue_script' );

With this plugin installed we can just paste the code from the extension with one difference, we should delete the script located at the end of every embed for quotebacks.

Building a Network Navigator

In 1987 Apple released this concept video of what they called a Network Navigator, an integrated agent to manage data from different sources, communicate with people across the network, and share information regardless of the source and its location.

It may seem quaint to us now, 30 years later but the real question is whether the tools have evolved to the point where it is possible to build such technology.

This post will explore some of the current technologies that would make such a project feasible and discuss potential future implementations. This is a theoretical exploration rather than a working example. There is no code in this post 🙂

Voice Enabled

Three things that were impossible to predict in ’87 are how powerful mobile phones would become, how much would agents become a common day occurrence, and the growth of home automation with tools like Alexa Home or Google Nest.

Tools like Google Assistant, Apple Siri, and Amazon Alexa have created large markets for voice-enabled applications so, in theory, it should be possible to do one of three things:

  • Use default tools from the agent
  • Load existing actions for what we need
  • Build custom actions when actions are not available or they will not do what we need

For example, Google Assistant can already do voice searches in Wikipedia and Gooogle search so we have the first layer of research tools available.

We can also leverage existing voice interfaces from universities or commercial research databases.

If there are no existing actions we can develop our actions to interact with data sources and libraries. We may also want to consolidate all searches into a single command.

Search for information

Today I can tell Google Assistant to search Wikipedia for bionics and it will display the results from Wikipedia for the requested term. How difficult would it be to trigger the same search on multiple sites/engines from the same command?

Machine Learning could help us to provide a context-sensitive search interface where results are more closely aligned with the meaning of our search.

Managing Documents

Another area where we might want to use Assistant is as input For Google Docs and other applications part of GSuite. Normally I wouldn’t go with just one provider but as far as I know, Google Docs, Slides, and Sheets are the only set that is scriptable with Javascript. This would allow us to create documents programmatically from an agent so that users have to tweak and finish them.

Text to Speech

Most browsers now have text-to-speech capabilities and so do agents. It shouldn’t be too much of a stretch to use the agent’s built-in speech capabilities or piggyback on the Web Speech API to provide a more generic solution that has the browser or agent read the page to us without us having to “make” an agent to do it.


WebRTC provides both audiovisual and data channel point to point and multicasting communication.

Agents could leverage the low-level APIs or they could piggyback into existing tools like Google Meet or similar tools that allow communication and data exchange.

Reviewing Lazy Loading in WordPress

In the not too distant future, WordPress will have lazy loading by default in WordPress core enabled by default. I believe that this will be merged into WordPress core for the 5.5 release; until then, development is done on a feature plugin for people to continue working on it.

But the problem is that this is enabled by default. As with many of the later decisions by the WordPress core team, this is good for beginners but I can think of at least two instances where lazy loading is not what I want to use:

  • Header images at the top of the page. If we want to minimize the load time then we shouldn’t lazy load them
  • Images and iframes above the fold that the user will see when the page first loads

There are several plugins that already do image and iframe lazyloading in my WordPress installation I have decided to use Jetpack and W3 Total Cache so I won’t lose lazy loading if I deactivate it in core.

Another thing to keep in mind is how does lazy loading affects existing image manipulation plugins such as Cloudinary?

I was thinking about enabling Jetpack’s lazy loading feature but while researching it I realized that it uses data-src attributes to handle lazy loading so I don’t know how it will interact with Cloudinary and the srcset attribute.

W3 Total Cache’s documentation and support leave a lot to be desired for the free version

Incorporating lazy loading without core

The first thing to do if you want to keep your code working as before is to disable lazy loading in WordPress Core with the wp_lazy_loading_enabled filter.

add_filter( 'wp_lazy_loading_enabled', '__return_false' );

We can then alter the content that gets inserted into the (classic) editor and will be published with the post.

The function will take the image from the attachment and the HTML from the image tag and use it to build a figure element with captioning, and alt attributes built from the content of the attachment page.

It also adds lazy loading that you can modify or remove based on your needs. As far as I know, you can’t remove the attribute once it’s been added to the image.

function html5_insert_image($html, $id, $caption, $title, $align, $url, $size, $alt) {
  $src  = wp_get_attachment_image_src( $id, $size, false );
  $html = get_image_tag($id, '', $title, $align, $size);
  $html5 = "<figure>";
  $html5 .= "  <img src='$url' alt='$alt' class='size-$size' loading='lazy' />";
  if ($caption) {
    $html5 .= "  <figcaption class='wp-caption-text'>$caption</figcaption>";
  $html5 .= "</figure>";
  return $html5;
add_filter( 'image_send_to_editor', 'html5_insert_image', 10, 9 );

The drawback of this method is that it only handles new images when using the classic editor, it will not go back and retroactively add the loading attribute to images already used in posts, if you need that done you’ll have to do it manually.

Good or Bad?

Whether native lazy loading is good or bad depends on what you need and what you’re using for image management and lazy loading (if anything). Having lazy loading in the core is not the only way to lazy load images, just what appears to be the most convenient because people just want things to work.

If you already use lazy loading from other sources you have to test carefully how they interact with your code and any image manager that you have installed.