Building a Custom WordPress Theme Update System

Not all WordPress themes are distributed through WordPress.org. In many professional setups, themes are released independently on the author’s own website. In these cases, WordPress does not provide automatic updates by default. To maintain a smooth user experience, a custom theme update system must be implemented.

Building a Custom WordPress Theme Update System

This guide explains how to build a manual WordPress theme updater using core APIs, with Init Press as a concrete example. The result integrates seamlessly into the native WordPress update workflow.

Overview of the Custom Theme Update Architecture

A standard custom theme update system consists of three core components:

  • The installed theme on the user’s WordPress site
  • A publicly accessible JSON file describing the latest version
  • A ZIP package containing the updated theme files

WordPress periodically checks for updates via internal transients. By hooking into these checks, a theme can announce its own updates without relying on WordPress.org.

The Version Information JSON File

The JSON file acts as a lightweight version API. It should be publicly accessible and return reliable metadata about the latest release.

{
    "name": "Init Press",
    "slug": "init-press",
    "version": "1.1.0",
    "author": "Init HTML",
    "homepage": "https://inithtml.com/theme/init-press/",
    "description": "A clean, lightweight, and UIkit-powered WordPress theme for blogs, content sites, and modern publications.",
    "requires": "6.3",
    "tested": "6.9",
    "requires_php": "7.4"
}

The version field is critical, as it is used to determine whether an update is available.

Hooking into WordPress Theme Updates

The update logic should run only in the admin area. WordPress exposes the site_transient_update_themes filter, which allows themes to register update data during the update check cycle.

/**
 * Theme updater
 */
if ( is_admin() ) {
    add_filter( 'site_transient_update_themes', function ( $transient ) {
        $theme_slug  = 'init-press'; // theme folder name
        $theme       = wp_get_theme( $theme_slug );

        if ( ! $theme->exists() ) {
            return $transient;
        }

        $current_ver = $theme->get( 'Version' );

        // Version metadata JSON
        $json_url = 'https://inithtml.com/releases/themes/init-press.json?nocache=' . time();

        $res = wp_remote_get( $json_url, [
            'timeout' => 10,
        ]);

        if ( is_wp_error( $res ) || wp_remote_retrieve_response_code( $res ) !== 200 ) {
            return $transient;
        }

        $info = json_decode( wp_remote_retrieve_body( $res ) );

        if ( ! $info || empty( $info->version ) ) {
            return $transient;
        }

        if ( version_compare( $info->version, $current_ver, '>' ) ) {

            if ( ! is_object( $transient ) ) {
                $transient = new stdClass();
            }

            $transient->response[ $theme_slug ] = [
                'theme'       => $theme_slug,
                'new_version' => $info->version,
                'url'         => $info->homepage ?? '',
                'package'     => 'https://inithtml.com/releases/themes/init-press.zip?ver=' . $info->version,
            ];
        }

        return $transient;
    });
}

How the Update Flow Works

The update process follows a clear sequence:

  • Retrieve the installed theme information using wp_get_theme()
  • Read the currently installed version from style.css
  • Fetch the remote JSON metadata via HTTP
  • Compare versions using version_compare()
  • Inject update data into the WordPress update transient

Once registered, WordPress displays the update notification and handles the download and installation automatically.

Understanding the Update Response Fields

  • theme: the theme directory slug
  • new_version: the latest available version
  • url: the theme’s homepage or changelog page
  • package: the ZIP file URL used for updating

The ZIP archive must contain a root folder matching the theme slug, otherwise the update will fail.

Production Considerations

  • Enable server-side caching for the JSON endpoint
  • Protect ZIP downloads using tokens or signed URLs if needed
  • Keep update logic strictly within the admin context
  • Always test updates on a staging environment first

Conclusion

A custom theme updater allows independently distributed WordPress themes to deliver a first-class update experience. By leveraging site_transient_update_themes, updates integrate naturally with WordPress core while remaining fully under the developer’s control.

This approach is essential for commercial themes, private client projects, and any WordPress ecosystem that requires reliable version management outside of WordPress.org.

Comments


  • No comments yet.

Init Toolbox

Press Ctrl + \ on desktop, or swipe left anywhere on mobile.

Login