Add custom CSS, JavaScript, HTML and PHP to your WordPress site — everywhere or on specific pages — without editing theme files, so nothing is lost when your theme updates.
Six code types
CSS, JavaScript, and HTML for the head, body or footer — plus optional PHP.
Priority control
Your CSS loads last in <head> and your JS last before </body>, so yours wins.
Instant cache purge
Saving a snippet clears 19 caching layers, so changes appear right away.
Device targeting
Show code on desktop, tablet or mobile only, with your own breakpoints.
Page targeting
Apply a snippet site-wide or limit it to chosen posts and pages.
Export / import
Move snippets and settings between sites as a single JSON file.
Requirements
Requirement
Version
WordPress
5.8 or later
PHP
7.4 or later (tested on 8.1 and 8.4)
User capability
manage_options — administrators only
Installation
Upload the wdd-custom-code-manager folder to /wp-content/plugins/, or install the ZIP via Plugins → Add New → Upload Plugin.
Activate the plugin from the Plugins screen.
Open Code Manager in the admin menu.
On activation the plugin creates its own database table, {prefix}wdd_ccm_snippets, so snippets are independent of your theme.
Updating from 1.0.0
Version 1.1.0 includes a database schema upgrade that runs on the first page load after you update. Take a database backup first, as you would with any schema change.
Quick start
Go to Code Manager → Add New.
Enter a Title and choose the code Type.
Paste your code into the editor.
Leave Load On as Entire Site, or pick Specific Pages.
Make sure Status is Active, then click Save Snippet.
Reload your site. The change is live — no manual cache clearing needed.
Example: hide a banner on mobile
/* Type: CSS Devices: Mobile only */
.promo-banner { display: none; }
Example: load an analytics tag in the head
<!-- Type: HTML Head Load On: Entire Site -->
<script async src="https://example.com/tag.js"></script>
Code types
Type
Where it is output
Notes
CSS
Inside one <style id="wdd-ccm-css"> block, immediately before </head>
Write plain CSS — no <style> tags. Device targeting is added as @media queries.
JavaScript
Inside one <script id="wdd-ccm-js"> block, immediately before </body>
Write plain JS — no <script> tags. Each snippet runs in its own function scope with its own error handler.
HTML Head
Inside <head>, before the CSS block
Raw HTML, output exactly as written. Use for meta tags, <link> and third-party tags.
HTML Body
Directly after the opening <body> tag
Uses wp_body_open; if the theme never fires it, the plugin inserts the code itself.
Every JS snippet is wrapped in (function(){ try { … } catch(e) { … } })();. A snippet that throws logs WDD CCM snippet #ID to the browser console and the remaining snippets still run. Because each snippet has its own function scope, top-level var/let declarations are not shared between snippets — attach anything you need to share to window.
Scope
The Load On setting controls which pages receive a snippet.
Option
Behaviour
Entire Site (global)
Output on every frontend page.
Specific Pages (specific)
Output only when the current page's ID is in the list. Use the search box to find posts and pages by title; IDs are stored comma-separated.
Matching uses the queried object
A Specific Pages snippet with no pages selected is never output. Matching compares against WordPress's queried object ID, which is a post ID on single posts and pages. Archive and search views have no post, so use Entire Site for those.
Device targeting
Tick Desktop, Tablet and/or Mobile to limit a snippet to those devices. Ticking none, or all three, means no restriction.
Breakpoints
Defined in Settings → Device Breakpoints.
Device
Width
Default
Desktop
≥ desktop breakpoint
1025 px and up
Tablet
≥ tablet breakpoint and < desktop breakpoint
768 – 1024 px
Mobile
< tablet breakpoint (calculated)
767 px and below
Breakpoints are clamped to 320–3840 px, and the tablet value is automatically held below the desktop value so the generated rules can never contradict each other.
How each type is targeted
Type
Method
Decided
CSS
Wrapped in an @media screen and (…) query
In the browser
JavaScript
Guarded by a window.innerWidth condition
In the browser, at load
HTML / PHP
Server-side wp_is_mobile() check — tablets count as mobile
On the server
Device-targeted HTML and full-page caching
HTML and PHP targeting is decided on the server, but a page cache stores one copy and serves it to everyone. If you use a full-page cache, use CSS or JavaScript for device-specific behaviour, or configure your cache to vary by device. CSS and JS targeting is unaffected because the browser decides.
Priority & load order
Making your code win
With Force Highest Priority on (the default), the plugin buffers the page and inserts your code after every theme and plugin hook has already run:
Nothing another plugin does on wp_head or wp_footer can land after your code, so your CSS wins the cascade against any rule of equal specificity, and your JavaScript runs last.
Load order cannot beat specificity
If a theme rule has a more specific selector than yours (for example body .site .btn against .btn), it still wins. Raise your selector's specificity, or add !important as a last resort.
Ordering your own snippets
Each snippet has a numeric Priority (default 10). Lower numbers load earlier. Snippets of the same type are output in priority order; ties are broken by snippet ID, so output order is always stable.
Hook mode fallback
If another plugin conflicts with page buffering, turn Force Highest Priority off. The plugin then outputs through wp_head, wp_body_open and wp_footer at PHP_INT_MAX — still last among normal hook callbacks.
When buffering is skipped
Admin screens, AJAX, cron, feeds, REST, XML-RPC and XML sitemaps.
Responses that are not an HTML document.
Requests where no active snippet needs buffering — you pay nothing on those.
Anything you exclude with the wdd_ccm_should_buffer filter.
Cache purging
Changes are meant to appear on the very next page load. Two things make that happen.
1. The plugin's own snippet cache
Active snippets are stored in a versioned object cache so the frontend avoids a database query on every request. Every write bumps the version number, so a stale entry can never be served. Where there is no persistent object cache, a transient is used instead.
2. Third-party page caches
The following are purged automatically whenever a snippet is saved, toggled, deleted, bulk-actioned or imported, and whenever settings change:
WP Rocket
LiteSpeed Cache
W3 Total Cache
WP Super Cache
WP Fastest Cache
Autoptimize
SiteGround Optimizer
Cache Enabler
Breeze
WP-Optimize
Hummingbird
Swift Performance
Nginx Helper
Kinsta
WP Engine
Pantheon
Cloudflare (official plugin)
Varnish HTTP Purge
Elementor
Manual purge
Admin bar → Purge Code Cache — available on every page while you are logged in.
Settings → Performance & Priority → Purge Now — also lists the layers detected on your site.
Administrator bypass
While logged in as an administrator, frontend pages are sent with no-cache headers and DONOTCACHEPAGE defined, so your own reloads always show the latest code.
Using a CDN or proxy that isn't listed?
Hook your own purge onto the wdd_ccm_purge_page_caches action — see Hooks & filters.
Settings
Found under Code Manager → Settings.
Setting
Default
What it does
Force Highest Priority
On
Buffers the page and writes CSS before </head> and JS before </body>. Turn off to use hook output instead.
Auto-purge caches on save
On
Purges every detected page cache whenever snippets or settings change.
Bypass cache for administrators
On
Sends no-cache headers and DONOTCACHEPAGE for logged-in administrators on the frontend.
Shown in Settings, but currently has no effect: a leading <?php and trailing ?> are always removed before execution, whatever this is set to.
Disable on Login Page
Off
Prevents snippets from loading on wp-login.php.
Desktop breakpoint
1025 px
Minimum width treated as desktop.
Tablet breakpoint
768 px
Minimum width treated as tablet. Mobile is everything below it.
Export / Import
Export
Under Code Manager → Export / Import, click Download Export File. You get one JSON file, wdd-ccm-export-YYYY-MM-DD-HHMMSS.json, containing every snippet and the plugin settings.
Choose or drop a previously exported file, pick a mode, and click Import Now.
Mode
Behaviour
Merge
Adds imported snippets alongside your existing ones.
Replace
Deletes all existing snippets first, then imports. This cannot be undone.
Tick Also import plugin settings from file to apply the file's settings as well. Caches are purged once, at the end of the import.
Notes
Snippets are re-created on import, so they receive new IDs. Specific-page targeting stores post IDs, which usually differ between sites — re-check the pages after migrating to a different site. Imported PHP snippets only execute when PHP snippets are enabled on the destination site.
PHP snippets
Security
PHP snippets run on your server through eval() with full WordPress privileges. Anyone who can edit a snippet can run arbitrary code on your site. Enable this only if you fully trust everyone with administrator access, and review every snippet you save.
Disabled by default. Turn on Enable PHP Snippets in Settings.
When disabled, PHP snippets cannot be saved and any existing ones do not run.
Snippets execute during wp_footer, after the page content. Anything they echo is printed at that point.
A fatal error in one snippet is caught and does not stop the page or the other snippets.
Errors are shown as an HTML comment to administrators, and written to error_log() when WP_DEBUG is on.
// A PHP snippet — wrapping <?php ?> tags are optional
add_filter( 'the_content', function ( $content ) {
return is_single() ? $content . '<p>Thanks for reading!</p>' : $content;
} );
Hooks added late may not fire
Because PHP snippets run on wp_footer, a hook registered there has already missed anything that ran earlier in the request, such as the_content on the current page. For logic that must run early, use a code-snippets or must-use plugin instead.
Hooks & filters
Filters
Filter
Arguments
Purpose
wdd_ccm_css_output
string $css
Modify the merged CSS before it is printed.
wdd_ccm_js_output
string $js
Modify the merged JavaScript before it is printed.
wdd_ccm_html_output
string $html, string $type
Modify merged raw HTML. $type is html_head, html_body or html_footer.
wdd_ccm_render_snippet
bool $render, object $snippet
Final say on whether a snippet renders on this request.
wdd_ccm_should_buffer
bool $should_buffer
Return false to disable page buffering for a request.
Actions
Action
Arguments
Fires
wdd_ccm_purge_page_caches
—
During a cache purge. Hook your own CDN or proxy here.
wdd_ccm_cache_flushed
int $new_version
After the snippet cache has been invalidated.
wdd_ccm_updated
string $from, string $to
Once after install or update. $from is an empty string on a fresh install.
Examples
// Skip a snippet on the checkout page
add_filter( 'wdd_ccm_render_snippet', function ( $render, $snippet ) {
if ( function_exists( 'is_checkout' ) && is_checkout() ) {
return false;
}
return $render;
}, 10, 2 );
// Purge a custom CDN whenever snippets change
add_action( 'wdd_ccm_purge_page_caches', function () {
my_cdn_purge_everything();
} );
// Don't buffer pages on a path where it causes trouble
add_filter( 'wdd_ccm_should_buffer', function ( $buffer ) {
return false !== strpos( $_SERVER['REQUEST_URI'], '/legacy-app/' ) ? false : $buffer;
} );
Troubleshooting
My changes don't show up
Check the snippet's Status is Active.
In Settings, confirm Auto-purge caches on save is on and your cache plugin is listed under Detected caching layers.
Click Purge Code Cache in the admin bar.
If you use a CDN or host-level cache that isn't listed, purge it once manually, then attach it to wdd_ccm_purge_page_caches.
Test in a private window — your browser may be caching the page or its assets.
My CSS is being overridden
Make sure Force Highest Priority is on. If it is, the other rule has higher specificity — see Priority & load order.
My JavaScript runs before an element exists
JavaScript is output just before </body>, so most of the page exists already. For elements added later, wait for the right moment:
document.addEventListener('DOMContentLoaded', function () {
// safe to query the DOM here
});
A script tag or code block is cut off
A literal </script inside a JavaScript snippet is escaped automatically to stop it closing the block early. If you need a script tag, add it as an HTML Footer snippet instead.
Two copies of my code are on the page
Each code type is printed once per request. If you see duplicates, check that the same code isn't also in your theme, or saved as two snippets.
Something conflicts with page buffering
Turn Force Highest Priority off. You keep automatic cache purging and the administrator bypass.
A JavaScript snippet isn't running
Open the browser console and look for WDD CCM snippet #… — a snippet that throws is reported there with its ID.
Uninstalling
Action
Result
Deactivate
Snippets stop loading and cached output is cleared. Your snippets and settings are kept.
Delete
The snippets table, all plugin options and any cached transients are permanently removed.
Export first
Deleting the plugin permanently removes your snippets. Download an export file before deleting if you might want them back.
Priority and cache-invalidation release. Snippets now reliably load after all other theme and plugin code, and edits appear on the frontend immediately instead of waiting for a page cache to expire.
Fixed
HTML Body snippets were output twice. The fallback gated on current_theme_supports( 'body-open' ), which is not a WordPress feature flag and so was always false — the output buffer always started andwp_body_open also fired. That path has been removed.
Saved changes did not appear on the frontend. The plugin performed no cache invalidation, so page caches kept serving HTML generated before the edit.
Snippet output could be beaten on load order. Hooks ran at wp_head 99 and wp_footer 99–101, so plugins hooking at 100 or later landed afterwards.
The database schema never upgraded.dbDelta() ran only on activation, which does not fire when a plugin is updated in place.
Contradictory device breakpoints were accepted. A tablet breakpoint at or above the desktop breakpoint produced rules that disagreed with each other. Values are now clamped and reordered on save.
Unstable order for tied priorities. Snippets sharing a priority could render in a different order per request; ties now fall back to id ASC.
Added
Force Highest Priority setting (default on) — buffers the page and writes CSS before </head> and JS before </body>, after every hook has run.
Auto-purge caches on save setting (default on) — supports 19 caching layers.
Bypass cache for administrators setting (default on).
Purge Code Cache in the admin bar, and a Purge Now button in Settings that lists detected caching layers.
Versioned snippet cache, removing the per-request database query from the frontend path.
Per-snippet try/catch around JavaScript, with errors logged to the console by snippet ID.
PHP snippet failures written to error_log() when WP_DEBUG is enabled.
Composite database index on (status, code_type, priority).
Update routine that runs on load when the stored version is behind the code, so in-place updates apply upgrades.