How To Prevent Common WordPress Theme Mistakes
If you’ve been thinking of creating free or premium WordPress themes, well, I hope I can help you avoid some of the mistakes I’ve made over the years. Even though I always strive for good clean code, there are pursuits that still somehow lead me into making mistakes. I hope that I can help you avoid them with the help of this article.
1. Don’t Gradually Reinvent The Wheel
Be careful when making things look nice — especially if you create a function that does almost exactly the same thing as another function just to wrap things nicely. The more prettifying code you add, the harder it gets to maintain. Keystrokes per minute isn’t the bottleneck of your performance as a developer when you spend most of your time thinking about code, not actually writing it.
I’ve made this mistake a lot thinking I was DRY-ing up the code.
For example, I made a function called get_portfolio_part($name, $slug)
. Can you guess what it did? Yes. It’s a wrapper to save me the ”hassle” of writing the extremely repetitive get_template_part(“portfolio/$name”, $slug);
. This is what I call “gradual wheel reinvention”. It does almost exactly the same thing as the original while complicating the code base at the same time.
Don’t do it! You don’t need to save those few keystrokes. It’s going to be difficult figuring out the actual path after a year has passed, or when someone else looks at your code. Even if you could argue that it’s simple and obvious — it’s going to either involve parsing yet another function in one's head or pure guesswork to guess where that function is fetching files from.
On top of saving a few characters, I remember the perfectly valid argument for making a get_portfolio_part()
function in my head — what if I decide to move the portfolio directory in the future? I’ll have to perform an “ugly search and replace.”
Can you guess how many times I have changed that directory name over the years? Zero. This brings us to mistake #2.
2. Stop Predicting The Future
Humans are terrible at predicting the future. Yet, as developers, we try to do it all the time.
For example, imagine you’ve made an option to display social icons somewhere in your post. Let's set aside the discussion whether that’s plugin territory or not. Just imagine that this is what you’ve decided to do. So our hypothetical function would look something like this:
function has_social_icon($icon) {
$icons = get_post_meta(get_the_ID(), 'post_social_icons', true);
// do what has to be done with $icons
return true;
}
A very typical idea crosses my mind now: "*But what if I want to use this function outside the loop sometime in the future*?" Well, this led me to a refactor that looks like something like this:
function has_social_icon($icon, $post_id = 0) {
if( ! $post_id ) {
$post_id = get_the_ID();
}
$icons = get_post_meta($post_id, 'post_social_icons', true);
// do what has to be done with $icons
return true;
}
And *voilà*! You’ve now created absolutely unnecessary bloat in the name of a non-existent future. This is such a simple example how these things happen, but the more complicated a thing becomes, the easier it is for you to lure yourself down a futuristic rabbit hole.
Don’t do it! Refactor out of real needs, not hypothetical scenarios that may or may not occur.
3. Premature Optimization Is The Root Of All Evil
Have you ever heard that quote? I didn’t give it much thought until rather recently. It’s very difficult to reform old habits, so this is something that trips me up to this day. I still catch myself optimizing code that I should not be optimizing.
Have you ever done something this?
<?php
$post_id = get_the_ID(); // look 'ma - I'm reusing ID, saving 1 function call!
$thumb = get_the_post_thumbnail( $post_id, 'large'); // look 'ma - I'm saving another function call! Yay!
?>
<div id="post-<?php echo $post_id ?>"
<?php if( $thumb ): ?>
<div class="thumbnail">
<?php echo $thumb ?>
</div>
<?php endif; ?>
</div>
Assigning a value to a variable, because you’re using that value twice is going to save you exactly .000002ms (a completely made up and useless figure), and 0ms when that request is cached, which it will be most of the time when performance is of concern anyway.
Here is much simpler way to write the same thing the WordPress way:
<div id="post-<?php the_ID() ?>"
<?php if( has_post_thumbnail() ): ?>
<div class="thumbnail">
<?php the_post_thumbnail('large') ?>
</div>
<?php endif; ?>
</div>
Yes, it involves two extra function calls, but the code performance benefit is negligible. That doesn’t meant that you shouldn’t optimize your code at all. Be intelligent about it. If you’re saving database queries, or you’re running expensive functions in a loop - of course you should keep your code optimized. But do it intelligently. Don’t throw everything in a variable just to save a function call. Speaking of variables…
4. Avoid Variables In Template Files
When you stop trying to over-optimize, you should notice considerably fewer variables in your template files. I recommend that you take that idea a step further and try to avoid variables in template files in general. Not because you should avoid variables themselves, but because of what they’re a symptom of in template files — logic.
While some logic will always be necessary, you can improve the readability of your template files significantly by removing as much as you can.
Here is a simple example.
<?php
$logo_url = false;
$thumbnail_url = wp_get_attachment_image_src( get_theme_mod( 'hypthetical_theme_logo' ), 'full' );
if( $thumbnail_url ) {
$logo_url = $thumbnail_url[0];
}
?>
<?php if( $logo_url ): ?>
<a href="<?php echo esc_url( home_url() ); ?>" title="<?php bloginfo( 'name' ); ?>" class="custom-logo">
<img src="<?php echo $logo_url; ?>" />
</a>
<?php endif; ?>
All by itself, this may not look terrible, but when it’s somewhere inside your `header.php` file, it’s going to look quite messy, especially when wrapped in multiple divs with indentation.
On top of it not looking great, why should the template (or the person reading the code) be concerned of how the logo is retrieved? Template files just want to show content, not fetch and parse the content.
Instead of defining two variables, why not extract them away into functions? Then the code above can easily turn into this:
<?php if( hypotheme_has_logo() ): ?>
<a href="<?php echo esc_url( home_url() ); ?>" title="<?php bloginfo( 'name' ); ?>" class="custom-logo">
<img src="<?php hypotheme_the_logo_url() ?>" />
</a>
<?php endif; ?>
This is much, much easier to read and avoids any unnecessary clutter and in case someone wants to know where the logo comes from — they can inspect the function instead. Now the logic is separate from the presentation.
Here is another example:
<?php
/**
* page.php
*/
?>
<?php get_header(); ?>
<?php
$hypotheme_sidebar = hypotheme_get_option( 'hypotheme_sidebar' );
$hypotheme_sidebar_size = hypotheme_get_option( 'hypotheme_sidebar_size' );
?>
<?php while ( have_posts() ) : the_post(); ?>
<div class="row two-columns">
<?php if ( $hypotheme_sidebar == 1 ): ?>
<div class="main-column <?php if ( $hypotheme_sidebar_size == 0 ) { ?> col-md-6 <?php } else { ?> col-md-7 <?php } ?>">
<?php else: ?>
<div class="main-column col-md-12">
<?php endif; ?>
<div id="page-<?php the_ID(); ?>" <?php post_class( 'entry-page' ); ?>>
<h1 class="entry-title"><?php the_title(); ?></h1>
<div class="entry-content"><?php the_content(); ?></div>
</div>
<?php if ( comments_open() ) : ?>
<?php comments_template(); ?>
<?php endif; ?>
</div>
<?php if ( $hypotheme_sidebar == 1 ) {
get_sidebar();
} ?>
</div>
<?php endwhile; ?>
<?php get_footer(); ?>
Look at those variables. On their own they’re not really out of place — they’re doing what they should be doing.
However, this template probably isn’t the only template in this theme with a sidebar. That means that those variables are probably present in all template files where there is a sidebar.
Not only the logic is mixed with the presentation, but it is also repeated all over the template files ( page.php, single.php, index.php, etc. ). That’s a lot of repetition, a lot of code that can be removed easily:
<?php
/**
* page.php
*/
?>
<?php get_header(); ?>
<?php while ( have_posts() ) : the_post(); ?>
<div class="row two-columns">
<div class="main-column <?php echo hypotheme_container_width_class() ?>">
<div id="page-<?php the_ID(); ?>" <?php post_class( 'entry-page' ); ?>>
<h1 class="entry-title"><?php the_title(); ?></h1>
<div class="entry-content"><?php the_content(); ?></div>
</div>
<?php if ( comments_open() ) : ?>
<?php comments_template(); ?>
<?php endif; ?>
</div>
<?php get_sidebar(); ?>
</div>
<?php endwhile; ?>
<?php get_footer(); ?>
This is way easier to read and understand. The reader doesn’t have to care about how you decide how wide is the container, yet if they’re interested — in most code editors you can quickly jump to that function and read all about it. Functions help make your code more readable and extendable if used in conjunction with either WordPress Hooks or the Pluggable Functions pattern.
Don’t be afraid to create multiple files where you can store all your necessary template functions, i.e. don’t dump everything inside functions.php
. By default, _s theme includes /inc/template-tags.php
file for that purpose. And if you find that the file becomes too large with all of the new template tags you’ve created, you can create more files as needed. It’s your theme after all!
5. Make Sure You’re Up To Date
WordPress is constantly evolving, just as everything else on the internet. Stay up to date with best practices, and question yourself every now and then, and also make sure you’re still using best practices.
For example, I’ve seen themes released on WordPress.org this year, that are still using wp_print_styles
instead of wp_enqueue_scripts
, even though wp_print_styles
has been deprecated since WordPress version 3.3.
If you’re building WordPress themes for others to use, keep up to date with best practices, and check the codex every now and then to see whether the way you’re doing something is still the best way to do it.
6. Use Native WordPress Functions When You Can
It’s important to use native WordPress functions when possible so that others can tap into your theme, either from a plugin or a child theme.
When you’re up to date with the latest and greatest that WordPress has to offer, you might discover that the example “Mistake # 4” can be completely replaced with native WordPress functions since WordPress version 4.5 because WordPress now supports Custom Logo functionality natively.
<?php if( has_custom_logo() ): ?>
<a href="<?php echo esc_url( home_url() ); ?>" title="<?php bloginfo( 'name' ); ?>" class="custom-logo">
<img src="<?php the_custom_logo() ?>" />
</a>
<?php endif; ?>
Here is another example.
When designing a nice post-to-post navigation without really giving it too much thought I resorted to using get_next_post function and copy-pasted something like this in my theme:
<?php
$next_post = get_next_post();
if (!get_next_post()): ?>
<a href="<?php echo esc_url( get_permalink( $next_post->ID ) ); ?>"><?php echo esc_attr( $next_post->post_title ); ?></a>
<?php endif; ?>
Sweet! The internet just wrote some code for me! This is exactly what I needed.
What’s wrong with this? Well, several things.
First of all, don’t access object properties directly when possible, unless you’re sure you have to. In this case, you can use get_the_title() function instead. This way you will properly retrieve the title, prepend “Private/Protected" and apply the_title
filter.
// do this
echo get_the_title( $next_post )
// instead of this:
echo $next_post->post_title
And secondly, there is a WordPress function called next post link and you can replace everything above with just a simple function call:
<?php next_post_link() ?>
Again, some research and staying up to date can help clean up themes significantly.
7. Don’t Build Your Own Framework
When I write code, I want it to be DRY, with a clean interface, reusable, and performant. I think ultimately we all want that.
When all those ambitions are combined with a sprinkle over premature optimization, a dash of future prediction, ignoring a native WordPress function or two, and the desire to save on a few keystrokes, that's when "a framework for me by me" is born.
As the saying goes, "The road to hell is paved with good intentions." In my almost five years of theme development, I’ve built a “solid framework” for myself at least twice and refactored them countless times. Now, I wish I could strip it all away in one fell swoop. Don’t do it! Spare your future self!
I’m against building "a framework for my by me," not frameworks in general. There are well supported and maintained frameworks out there, like the Genesis theme or Sage by Roots. But they are not in the "a framework by me for me" format.
Here are a few problems and side effects of building a framework for yourself:
Maintainability Issues
The first problem is that building a “framework” is just adding an additional codebase to maintain. Especially if the framework lives in your /inc/me-framework
directory, you will have to update all your themes using that framework when you release an update to it.
If you decide not to update your framework in each theme every time you update it, there is still trouble lurking around the corner.
As you grow as a developer, your framework will grow and change, too. Eventually leading to incompatibility with your old themes. What if you discover a critical bug in older framework versions? You’ll have to either rewrite parts of all themes you’ve made or make a very special bug-fixed fork. And again: more code to maintain.
Plugin Territory
If you find yourself adding “custom functionality” in the theme, you might want to create a WordPress plugin instead. Themes should make pretty layouts and style them. Theme files should be filled with configuration, attaching to hooks and using template tags that either plugins or WordPress core provide. If you feel the need for using PHP Classes, you’re probably venturing into the plugin territory.
Make a plugin instead; make it easily customizable and style it in your theme. Not only you will avoid making a framework, but you’ll also contribute to back to the open-source community!
Increased Complexity
When you build a framework for yourself you’re making your theme more complex and difficult to work with. When someone reads your theme code, they’ll have to learn your framework that’s most likely either poorly documented or undocumented at all.
Conclusion
I have come to realize that most of the mistakes I’ve made have been caused by either the desire to save time in the future (xkcd has a wonderful comic about that) or to improve the code itself in some way, either by following a best practice that I’ve read somewhere or making the code look nicer.
WordPress has its own coding and theming standards. While you can write PHP the way you want to in your template files, it’s best to actually stick to “the WordPress way,” even if it’s not necessarily “the best way.” Remember that “best” is relative to the audience. When in Rome, do as the Romans do.
So please, don’t repeat my mistakes. I truly hope that this article will help you create great WordPress themes!
Further Reading
- WordPress Full-Site Editing: A Deep Dive Into The New Feature
- WordPress Playground: From 5-Minute Install To Instant Spin-Up
- How To Reduce The Need To Hand-Code Theme Parts In Your WordPress Website
- Vanilla JavaScript, Libraries, And The Quest For Stateful DOM Rendering