Kjeks
← All docs

Build an add-on

Wire any third-party technology into the Kjeks consent layer, using the Google Consent Mode add-on (kjeks-google) as the worked example.

Requires the Kjeks corePHP 8.3+Source on GitHub →

A Kjeks add-on is just a normal WordPress plugin that declares its technologies to the core plugin. Kjeks then holds those scripts back until the visitor grants the matching consent category. You never write the blocking yourself — you describe what runs and which category it belongs to. Everything below maps to real code inkjeks-google.

1 · Anatomy of an add-on

Plugin header

Declare a dependency on the core plugin so WordPress itself warns when it's missing, and mark it network-capable:

<?php
/**
 * Plugin Name:       Kjeks Google
 * Description:       Google Tag Manager / GA4 for Kjeks via Consent Mode v2.
 * Requires at least: 6.8
 * Requires PHP:      8.3
 * Requires Plugins:  kjeks          // WordPress warns if Kjeks is missing
 * Network:           true           // multisite-friendly, like the core plugin
 */

Boot only when Kjeks is present

The Requires Plugins: kjeks header already stops WordPress from activating your add-on without Kjeks (and from deactivating Kjeks while yours is active). So you don't need a dependency class — a one-linefunction_exists() guard on the core plugin's public API is enough insurance for the rare “active but not loaded” edge:

add_action(
    'plugins_loaded',
    static function (): void {
        // The Requires Plugins header prevents activation without Kjeks; this
        // one-liner is cheap insurance for the "active but API not loaded" edge.
        if ( ! function_exists( 'kjeks_register_integration' ) ) {
            return;
        }
        Plugin::instance()->boot();
    }
);
Registration hangs off the kjeks_register_integrations action, whichonly the core plugin fires — so even without the guard you can't fatal by calling kjeks_register_integration() when Kjeks is absent. Kjeks loads its API on init (priority 20 fires that hook), and booting on plugins_loaded keeps the ordering correct.

2 · Register your technology

This is the heart of every add-on. Hook thekjeks_register_integrations action and callkjeks_register_integration( $id, $args ). Kjeks blocks the scripts until the category is granted:

add_action( 'kjeks_register_integrations', function (): void {
    kjeks_register_integration(
        'google-tags',                    // unique integration id
        array(
            'category'    => 'analytics', // gate: runs only when this is granted
            'label'       => 'Google Tag Manager / Analytics',
            'src_scripts' => array(
                'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX',
            ),
            'inline'      => array(
                "gtag('js', new Date()); gtag('config', 'G-XXXXXXX');",
            ),
            // 'handles' => array( 'my-registered-handle' ), // or gate an already-enqueued script
        )
    );
} );

The arguments

KeyWhat it does
categoryThe gate: preferences, analytics, or marketing. Unknown or necessary falls back to marketing.
labelHuman name shown in the cookie declaration.
src_scriptsExternal scripts to inject after consent (a URL string, or { src, attrs }).
inlineInline JS snippets to run after consent.
handlesRegistered script handles to hold back — gate a script another plugin/theme enqueued.
Prefer one-liners? The core also exposes kjeks_enqueue_script(),kjeks_add_inline_script(), and kjeks_embed() — see thecore 201 guide.

3 · React to consent on the front end

Registration is enough for most add-ons — Kjeks loads your scripts when consent is granted. But some technologies (like Google Consent Mode) must alsosignal on every change. Kjeks gives you a small JS surface:window.kjeks.isGranted( category ) and thekjeks:granted / kjeks:withdrawn events.

// Consent Mode v2 defaults — printed in wp_head BEFORE anything Google loads.
gtag( 'consent', 'default', {
    ad_storage: 'denied', ad_user_data: 'denied',
    ad_personalization: 'denied', analytics_storage: 'denied',
    wait_for_update: 500,
} );

// Sync to Kjeks now and on every change.
function granted( c ) {
    return window.kjeks ? window.kjeks.isGranted( c ) : false;
}
function apply() {
    if ( typeof gtag !== 'function' ) return;
    gtag( 'consent', 'update', {
        analytics_storage: granted( 'analytics' ) ? 'granted' : 'denied',
        ad_storage:        granted( 'marketing' ) ? 'granted' : 'denied',
    } );
}
window.addEventListener( 'kjeks:granted', apply );
window.addEventListener( 'kjeks:withdrawn', apply );
document.addEventListener( 'DOMContentLoaded', apply );

kjeks-google prints the first block (defaults denied) in wp_head at priority 1, and the sync block at priority 2 — so Google's own state isdenied until Kjeks says otherwise, a second layer on top of the gate.

4 · Settings, config & updates

Store configuration

kjeks-google keeps a network default (kjeks_google_network) and a per-site override (kjeks_google), resolved so a site's own value wins. Expose a settings page on network_admin_menu (capabilitymanage_network_options) and admin_menu (capabilitymanage_options), mirroring how the core plugin adapts to single-site vs multisite.

Offer a filter

Let other code adjust your resolved config at runtime:

add_filter( 'kjeks_google_config', function ( $config, $blog_id ) {
    // $config = [ 'gtm_id' => '', 'ga4_id' => 'G-XXXX', 'gating_category' => 'analytics' ]
    return $config;
}, 10, 2 );

Ship self-updates (optional)

Add soderlind/wordpress-github-updater via Composer and callGitHubUpdater::init() in your main file, exactly like the rest of the family — see any add-on's bootstrap for the pattern.

Checklist:Requires Plugins: kjeks header · ② one-line function_exists() guard · ③ register onkjeks_register_integrations · ④ (if needed) sync via kjeks:granted/kjeks:withdrawn · ⑤ settings + a config filter · ⑥ verify with thescanner that nothing loads before consent.

Study the full example in the kjeks-google repository, and see the core developer guide for the complete integration API.