WDD Custom Code Manager
v1.1.0

WDD Custom Code Manager

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

RequirementVersion
WordPress5.8 or later
PHP7.4 or later (tested on 8.1 and 8.4)
User capabilitymanage_options — administrators only

Installation

  1. Upload the wdd-custom-code-manager folder to /wp-content/plugins/, or install the ZIP via Plugins → Add New → Upload Plugin.
  2. Activate the plugin from the Plugins screen.
  3. 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

  1. Go to Code Manager → Add New.
  2. Enter a Title and choose the code Type.
  3. Paste your code into the editor.
  4. Leave Load On as Entire Site, or pick Specific Pages.
  5. Make sure Status is Active, then click Save Snippet.
  6. 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

TypeWhere it is outputNotes
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.
HTML Footer Before </body>, before the JS block Raw HTML, output exactly as written.
PHP Executed during wp_footer Disabled by default. See PHP snippets.
JavaScript error isolation

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.

OptionBehaviour
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.

DeviceWidthDefault
Desktop≥ desktop breakpoint1025 px and up
Tablet≥ tablet breakpoint and < desktop breakpoint768 – 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

TypeMethodDecided
CSSWrapped in an @media screen and (…) queryIn the browser
JavaScriptGuarded by a window.innerWidth conditionIn the browser, at load
HTML / PHPServer-side wp_is_mobile() check — tablets count as mobileOn 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:

Theme & plugin <head> output→ HTML Head→ CSS→ </head>
Page content & footer scripts→ HTML Footer→ JavaScript→ </body>

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 RocketLiteSpeed CacheW3 Total CacheWP Super Cache
WP Fastest CacheAutoptimizeSiteGround OptimizerCache Enabler
BreezeWP-OptimizeHummingbirdSwift Performance
Nginx HelperKinstaWP EnginePantheon
Cloudflare (official plugin)Varnish HTTP PurgeElementor

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.

SettingDefaultWhat it does
Force Highest PriorityOnBuffers the page and writes CSS before </head> and JS before </body>. Turn off to use hook output instead.
Auto-purge caches on saveOnPurges every detected page cache whenever snippets or settings change.
Bypass cache for administratorsOnSends no-cache headers and DONOTCACHEPAGE for logged-in administrators on the frontend.
Enable PHP SnippetsOffAllows PHP snippets to execute. See PHP snippets.
Auto-strip PHP open/close tagsOffShown 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 PageOffPrevents snippets from loading on wp-login.php.
Desktop breakpoint1025 pxMinimum width treated as desktop.
Tablet breakpoint768 pxMinimum 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.

{
  "plugin":   "WDD Custom Code Manager",
  "version":  "1.1.0",
  "exported": "2026-10-10 14:30:00",
  "settings": { "force_priority": 1, "desktop_breakpoint": 1025, … },
  "snippets": [
    { "title": "Hide promo on mobile", "code_type": "css", "scope": "global",
      "device_visibility": "mobile", "status": 1, "priority": 10, "code": "…" }
  ]
}

Import

Choose or drop a previously exported file, pick a mode, and click Import Now.

ModeBehaviour
MergeAdds imported snippets alongside your existing ones.
ReplaceDeletes 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

FilterArgumentsPurpose
wdd_ccm_css_outputstring $cssModify the merged CSS before it is printed.
wdd_ccm_js_outputstring $jsModify the merged JavaScript before it is printed.
wdd_ccm_html_outputstring $html, string $typeModify merged raw HTML. $type is html_head, html_body or html_footer.
wdd_ccm_render_snippetbool $render, object $snippetFinal say on whether a snippet renders on this request.
wdd_ccm_should_bufferbool $should_bufferReturn false to disable page buffering for a request.

Actions

ActionArgumentsFires
wdd_ccm_purge_page_caches—During a cache purge. Hook your own CDN or proxy here.
wdd_ccm_cache_flushedint $new_versionAfter the snippet cache has been invalidated.
wdd_ccm_updatedstring $from, string $toOnce 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

  1. Check the snippet's Status is Active.
  2. In Settings, confirm Auto-purge caches on save is on and your cache plugin is listed under Detected caching layers.
  3. Click Purge Code Cache in the admin bar.
  4. 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.
  5. 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

ActionResult
DeactivateSnippets stop loading and cached output is cleared. Your snippets and settings are kept.
DeleteThe 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.

File structure

wdd-custom-code-manager/
├── wdd-custom-code-manager.php      Plugin header, constants, bootstrap, update routine
├── uninstall.php                    Cleanup on plugin deletion
├── readme.txt                       WordPress.org readme and changelog
├── CHANGELOG.md                     Developer changelog
├── docs/
│   └── index.html                   This documentation
├── includes/
│   ├── class-wdd-ccm-db.php         Table creation and schema versioning
│   ├── class-wdd-ccm-snippets.php   Snippet CRUD, bulk actions, cache invalidation
│   ├── class-wdd-ccm-settings.php   Settings storage and sanitising
│   ├── class-wdd-ccm-cache.php      Snippet cache and page-cache purging
│   ├── class-wdd-ccm-frontend.php   Frontend output and load-order handling
│   ├── class-wdd-ccm-export-import.php   JSON export and import
│   └── class-wdd-ccm-admin.php      Menus, assets, form and AJAX handlers
└── admin/
    ├── css/admin.css
    ├── js/admin.js
    └── views/                       snippets-list, snippet-editor, settings, export-import

Database table

ColumnTypeNotes
idBIGINT, auto-incrementPrimary key
titleVARCHAR(255)
descriptionTEXTOptional notes
codeLONGTEXTStored as written
code_typeVARCHAR(50)css, js, html_head, html_body, html_footer, php
scopeVARCHAR(20)global or specific
post_idsTEXTComma-separated IDs for specific scope
device_visibilityVARCHAR(100)all, or a comma-separated list of desktop, tablet, mobile
statusTINYINT(1)1 active, 0 inactive
priorityINT(11)Lower loads earlier
created_at, updated_atDATETIME

Changelog

Follows Keep a Changelog and Semantic Versioning.

1.1.0

Latest

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 and wp_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.
  • Filters wdd_ccm_css_output, wdd_ccm_js_output, wdd_ccm_html_output, wdd_ccm_render_snippet, wdd_ccm_should_buffer; actions wdd_ccm_purge_page_caches, wdd_ccm_cache_flushed, wdd_ccm_updated.

Changed

  • Output uses two explicit, mutually exclusive modes (buffer and hook) with a per-type guard so they can never duplicate each other.
  • PHP snippets still execute on wp_footer, since they may register hooks or enqueue assets.
  • A literal </script inside a JavaScript snippet is escaped so it cannot close the output block early.
  • Importing purges caches once on completion rather than once per row.
  • Deactivation clears cached snippet output.
  • uninstall.php also removes the cache-version and plugin-version options and leftover transients.

1.0.0

Initial release

Added

  • Custom CSS, JavaScript, HTML (head, body, footer) and PHP snippets.
  • Syntax-highlighted editor using WordPress's bundled CodeMirror.
  • Global or per-page scope with post and page search.
  • Device targeting for desktop, tablet and mobile with configurable breakpoints.
  • Per-snippet priority ordering and active/inactive status.
  • Bulk activate, deactivate and delete.
  • Optional PHP execution, disabled by default.
  • JSON export and import in merge or replace mode, with optional settings.
  • Custom database table with its own schema versioning.