nitida — Theme Mods Cache
WordPress must-use plugin: unserializes the active theme's theme_mods option once per request instead of on every get_theme_mod() call.
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/lintmycode/wp-mu-theme-mods/archive/refs/heads/main.zipnitida/wp-mu-theme-mods
WordPress must-use plugin, installed with Composer, that unserializes the
active theme's theme_mods_* option once per request instead of on every
get_theme_mod() call. Bedrock's autoloader loads it; there is nothing to
activate and nothing to configure.
Why
get_option() caches the raw serialized string and runs maybe_unserialize()
on every call. get_theme_mod() goes through it every time. Kadence calls
get_theme_mod() ~1,100 times per page, so a large option gets parsed ~1,100
times per uncached request. On k.borealis.travel (90KB option, 2 vCPUs) that
was the top frame in the PHP slow log during the 2026-10-02/03 bot slowdowns.
With the plugin, local page time halved (3.7s → 1.85s) and the rendered HTML
stayed byte-identical.
Does a site need it?
Worth it when the option is big and the theme reads it a lot (Kadence, Astra, GeneratePress with header/footer builders), and especially on a small box that sees uncached bursts. A 5KB option or a fully page-cached site gains little.
wp db query "SELECT option_name, LENGTH(option_value) FROM wp_options WHERE option_name LIKE 'theme_mods_%'"
Rule of thumb: over ~20KB for the active theme → install.
Install (Bedrock)
composer config repositories.wp-mu-theme-mods vcs https://github.com/lintmycode/wp-mu-theme-mods.git
composer require nitida/wp-mu-theme-mods:^1.0
composer/installers puts it in web/app/mu-plugins/wp-mu-theme-mods/. Make
sure that directory is ignored, and delete any hand-copied
theme-mods-cache.php in mu-plugins/ so the hooks are not registered twice.
Behaviour
- The first read of the request runs normally,
option_*filters included; the result is returned frompre_option_*afterwards. - Any add/update/delete of the option in the same request drops the kept copy,
so
set_theme_mod()followed byget_theme_mod()returns the new value. - Customizer preview is unaffected: its overrides run in
theme_mod_{$name}, after this. A theme-switch preview reads another option name and is simply not cached.