The Most Common WordPress Theme Development Mistakes (and How to Fix Them)

Submitting a theme to the WordPress.org theme directory is a great way to share your work and contribute to the WordPress community. Currently, there are over 7000 themes in the directory, the most popular of which exceeds 300,000 active installations. (Not including Twenty____ Themes which are packaged with WordPress and have install counts in the millions.)

Before submitting your theme to the directory, it’s important to understand the review process first because if your theme doesn’t meet those requirements it can be rejected on the spot.

Themes that have 3 or more distinct issues may be closed as not-approved. However, theme authors may resubmit the theme once they’ve corrected the issues.

https://make.wordpress.org/themes/handbook/review/required/

Reviewers are on your side and want to see your theme go live, once it meets the standards required. If your theme has only minor issues preventing it being included in the directory, your reviewer will work with you to fix those.

Unfortunately, if your theme has too many issues it will be closed as not-approved. If you decide to fix the issues you can upload the theme again – but it will join the back of the queue.

From my experience reviewing over 100 themes I’ve been able to identify the most common issues that prevent themes being approved. By sharing these with you in this article I’m hoping I can help you avoid getting stuck in the queue or rejected.

Uploading Your Theme

When you upload a theme, it joins the queue to be reviewed. On average it will take two months for your theme to reach the front of the queue and receive its first review. All reviewers are volunteers with limited time available to complete reviews. A variety of factors can affect the wait time. When more people volunteer to review themes, the queue moves quickly. Conversely, when themes with a lot of issues are submitted it slows down the queue.

By submitting a theme that meets all the requirements it makes the review process a lot smoother and ultimately your theme will be live sooner. In this guide, we are going to explore the most common issues that will keep your theme held up in the queue and prevent it from being approved.

Note: Theme authors that have a track record of submitting issue-free themes can apply to become ‘Trusted Authors‘.

Naming Issues

When you upload a theme, the first check that is performed is to see if the name is already taken. Frequently you will be told the name you’ve chosen is already taken, even if you can’t see a theme with that name in the directory.

How could that be? The reason is that the test isn’t checking against just the directory, it’s checking against the entire WordPress ecosystem. If a theme has been released anywhere (Github, ThemeForest, etc.) and has over 50 active installations, that name will be unavailable to use.

Note: if you’ve released your theme elsewhere and accumulated 50+ installations, you can still use that name in the directory.

Unescaped Output

Theme reviewers take security very seriously, there’s even a dedicated resource. An entire article could be written on writing secure themes, but in this section we are going to explore one aspect: escaping output.

Unescaped output places users of your theme at risk. Here’s an example of an unescaped value ($title):

$title = get_option( 'my_custom_title' );
echo '<h2>' . $title . '</h2>';

The problem with the above is that while we know what type of value $title should be, a string, we have not checked if that is the case.

If a hacker has managed to change the value of ‘my_custom_title’ in the database, your theme will output that value. This presents a huge risk as they could replace the intended output with inline Javascript:

    alert('This is dangerous'); 

The solution is to escape all output to ensure it only includes the type of data we are expecting.

Our example could be fixed like this:

$title = get_option( 'my_custom_title' );
echo '<h2>' . esc_html( $title ) . '</h2>';

The downside to using esc_html is that it strips all HTML tags. If $title included bold or italics, for example:

$title = 'This article is <strong>very</strong> useful';
echo esc_html( $title );

The word ‘very’ would not be bold on the frontend; instead it would output the code <strong>very</strong>.

This illustrates why it’s important to use the correct escaping functions for the context. If we were expecting some HTML in the output, we’d be better using wp_kses_post() or wp_kses() and setting the $allowed_html parameter.

Functions that output also need to be escaped:

<a href="<?php echo esc_url( get_permalink() ); ?>">

The exception is WordPress core functions that include ‘the_’ in their name, these are usually escaped already.

function the_permalink( $post = 0 ) {
    /**
     * Filters the display of the permalink for the current post.
     *
     * @since 1.5.0
     * @since 4.4.0 Added the `$post` parameter.
     *
     * @param string      $permalink The permalink for the current post.
     * @param int|WP_Post $post      Post ID, WP_Post object, or 0. Default 0.
     */
    echo esc_url( apply_filters( 'the_permalink', get_permalink( $post ), $post ) );
}

Untranslatable Text

To be accepted into the directory all themes must be 100% ‘translation-ready’. That means each text string your theme outputs must be translatable.

WordPress already has the systems and functionality to handle the translation process, you just need to make sure your strings use the correct functions.

While simple to implement, this is often overlooked as it goes against the flow of how people write HTML.

Normally, you might do something like this:

<h1>404 - Not Found</h1>

To make it translatable, you need to add in some PHP:

// __ functions are the basis of localization.
<h1><?php echo __( '404', 'text-domain' ); ?>

// _e functions echo the value.
<h1><?php _e( '404', 'text-domain' ); ?>

// Escape and echo the string.
<h1><?php esc_html_e( '404', 'text-domain' ); ?>

// localization and variables.
<h1><?php _n( 'One post', '%s posts', $count, 'text-domain' ); ?>

Strings output by functions must also be translation ready:

// not translation-ready :-(
<?php next_posts_link( 'Older Entries' ); ?>

// translation-ready :-)
<?php next_posts_link( esc_html__( 'Older Entries', ‘text-domain’ ) ); ?>

Tip: A lot of code examples in codex.wordpress.org don’t use the translation functions, so be careful when copy and pasting those.

Incorrectly Enqueuing Resources

The .css and .js files your theme uses must be enqueued using the correct functions: wp_enqueue_style() for CSS and wp_enqueue_script() for Javascript.

A common error is to hardcode scripts and styles directly into the <head> or before </body>. There are two problems to this approach:

1. Impossible to remove

If a plugin needs to remove a resource you have loaded, it’s not possible. If you had used the proper enqueue functions it could be done like so:

/**
 * Dequeue the theme javascript.
 *
 * Hooked to the wp_enqueue_scripts action, with a late priority (100),
 * so that it is after the script was enqueued.
 */
function wptavern_dequeue_script() {
   wp_dequeue_script( 'theme-scripts' );
}
add_action( 'wp_enqueue_scripts', 'wptavern_dequeue_script', 100 );

2. Duplicate Loading

If you enqueue a resource, jQuery for example, and a plugin also enqueues it, WordPress is smart enough to only load it once.

/**
 * Enqueue jQuery
 *
 * jQuery will only be loaded once, despite the two enqueues.
 * jQuery is packaged with WordPress so we don't need to specify a src. 
 */
function wptavern_enqueue_script() {
   wp_enqueue_script( 'jquery' );
   wp_enqueue_script( 'jquery' );
}
add_action( 'wp_enqueue_scripts', 'wptavern_enqueue_script' );

If instead you had hardcoded jQuery into your <head> then there would be no way for WordPress to know, and it would be loaded twice.

Plugin-Territory Functionality

The scope of a theme should only handle the design and aesthetic of a website, all other functionality should be handled by WordPress itself or plugins.

In an attempt to add more value to their themes, theme authors often try to incorporate extra functionality, for example, SEO controls or custom post types.

The problem with bundling functionality into a theme is that the data is not portable. Take SEO controls as an example, if the user changes the theme, they lose all the work they did to optimize their pages. In contrast by using an SEO plugin, the data and functionality is independent of the theme and will be retained when changing the theme.

Some examples of plugin-territory functionality:

  • Analytics/Tracking
  • SEO controls
  • Contact Forms
  • Shortcodes
  • Gutenberg Blocks

Tip: If your code writes to the database, it is highly likely to be plugin territory. The exception would be design-related settings (sidebar position, colors, etc.).

Not Prefixing

Prefixing is a way of ensuring that your code doesn’t clash with code from plugins. Namespacing in PHP is a better way to achieve the same effect. However, some users are still using old versions of PHP (5.2) which don’t support that feature.

Justin Tadlock shared a list of common things that should be prefixed:

  • PHP function names.
  • PHP class names.
  • PHP global variables.
  • Action/Filter hooks.
  • Script handles.
  • Style handles.
  • Image size names.

Source: https://themereview.co/prefix-all-the-things/

// function example.
my_prefix_example();

// class example.
class My_Prefix_Example { … }

// action and filter example.
do_action( 'my_prefix_action' );
apply_filters( 'my_prefix_filter', $values );

// enqueue examples.
wp_enqueue_script( 'my_prefix_script', get_template_directory_uri() . '/js/custom-script.js' );
wp_enqueue_style( 'my_prefix_style', get_template_directory_uri() . '/css/styles.css' );

// image size example.
add_image_size( 'my_prefix_image_size', 220, 180 ); // 220 pixels wide by 180 pixels tall.

Exception: When enqueuing third-party resources, don’t add a prefix:

 // enqueuing a third-party script (chosen.js).
 wp_enqueue_script( 'chosen', get_template_directory_uri() . '/js/chosen.js' );

Licensing Issues

Your theme and all of its files must be 100% GPL-compatible. This includes images, libraries, scripts, and fonts.

All third-party resources must list their source and license information.

This requirement can be particularly tricky as not all licenses are GPL-friendly. The Unsplash license only has one restriction:

“This license does not include the right to compile photos from Unsplash to replicate a similar or competing service.”

That one restriction, however, is enough to make it non-GPL-compatible, and as such, you won’t see Unsplash images included in wordpress.org themes.

A list of GPL-compatible licenses is available here – https://www.gnu.org/licenses/license-list.html#GPLCompatibleLicenses

Recently, stocksnap.io has been the most common source of images in the directory as all the images they list are licensed as CC0 (GPL-compatible).

Screenshot Mistakes

The requirements state that your screenshot should be an unedited representation of your theme that doesn’t look like an advertisement. That means no photoshop work, overlays, borders or fancy effects.

Images must also follow the same licensing requirements we explored above.

theme pictured: Blocksy

Bonus: Use a Coding Standard

Code that seems easy to read and understand for you, can be the complete opposite for a reviewer who only has 10-15 minutes to check your code.

While there is no requirement on coding standards, following one does make your code easier to read, understand and maintain. I personally use and recommend the ‘WordPress Coding Standards‘, though there are others.

Using PHP_CodeSniffer and the WordPress ruleset in your code editor can make adhering to a standard a lot easier – https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards

Conclusion

The Theme Requirements are created with the end user in mind. Avoid making the common mistakes I’ve listed above and your theme will be approved in no time. If you would like to experience the review process from the other side, you can even become a reviewer.

Would you like to write for WP Tavern? We are always accepting guest posts from the community and are looking for new contributors. Get in touch with us and let's discuss your ideas.

3 Comments


  1. Note: if you’ve released your theme elsewhere and accumulated 50+ installations, you can still use that name in the directory.

    If your theme has more than 50+ active install, it will give you an error and you will not be able to upload the theme.

    What are the procedures to upload the theme that has 50+ active install ??

    Report

    Reply

      1. This is correct. I’m happy to take a look at these cases manually and handle them for you.

        Report

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.