JavaScript Modules
Script loader
What the script loader does
The script loader adds a vendor's <script> to the page once the script's
category is allowed, and removes the element again when the visitor withdraws
it. @c15t/browser and createConsentRuntime create one from their scripts
option. Create one yourself only for a kernel you built with
createConsentKernel.
The scripts guide shows where the list
goes in each setup. The helpers in @c15t/integrations return Script objects for
common vendors; each integration guide covers
one.
Script fields
| Field | Type | Default | What it does |
|---|---|---|---|
id | string | required | A unique, stable name. The loader tracks the script by it. |
category | category or condition | required | What must be allowed, such as 'measurement' or { and: ['measurement', 'marketing'] }. |
src | string | none | The URL to load. |
resourceKey | string | id | The DOM resource identity. Matching keys share one element within and across loaders, while each logical script keeps its callbacks and consent state. Use different keys for different resources under the same script ID. |
textContent | string | none | Inline code to run instead of src. |
callbackOnly | boolean | false | Add no element, run only the callbacks. For a vendor already on the page. |
alwaysLoad | boolean | false | Load whatever the consent state, for tags that manage consent themselves through the vendor's API. |
observeConsentBeforeLoad | boolean | false | Notify onConsentChange during initial denial and subsequent updates before the resource loads. Useful for integrations that coordinate a shared SDK. |
persistAfterConsentRevoked | boolean | false | Keep the element after withdrawal instead of removing it. |
target | 'head' | 'body' | 'head' | Where the element goes. |
async, defer | boolean | none | Attributes on the created element. |
nonce | string | the runtime's nonce | CSP nonce for this element. |
fetchPriority | 'high' | 'low' | 'auto' | none | Fetch priority hint. |
attributes | Record<string, string> | none | Other attributes for the element. |
anonymizeId | boolean | true | Give the element a random id, so content blockers cannot match it by name. |
vendor | string | none | A vendor slug for vendor-level consent. The script also waits while the visitor has turned that vendor off. |
vendorId, iabPurposes, iabLegIntPurposes, iabSpecialFeatures | IAB IDs | none | Under an IAB policy, the TC string decides instead of the category. |
Lifecycle callbacks
Each callback receives { id, elementId, hasConsent, consents, element?, error?, vendor? }.
| Callback | When it runs |
|---|---|
onBeforeLoad | Before a load attempt. It can run again for the same script if consent changes before the load starts, so make it safe to repeat. |
onLoad | After the script loaded. Use it for set-up that needs the vendor's code. |
onError | When the script failed to load. |
onConsentChange | Each time a loaded script's consent changes. With observeConsentBeforeLoad, also during initial denial and later changes before loading. Use it for a vendor's opt-out call when you turn off the reload. |
onDispose | When the configuration is removed or the loader is disposed, including a script that never loaded. |
Withdrawal alone does not call onDispose. With the default reload, the page
reloads after withdrawal anyway; with reloadOnConsentRevoked: false, use
onConsentChange to stop the vendor.
When scripts share an element, each active registration receives its own
onLoad or onError callback. A registration that joins after c15t observed
completion receives that callback in a microtask, after its consent callback,
using its current callbacks and consent. Inline scripts share their deferred
completion too. Removing a registration cancels its pending callback. c15t does
not infer completion for elements added outside the loader.
Removing the original loader transfers ownership to a remaining loader. c15t removes an element it created when the last loader releases it.
Update the list
loader.updateScripts(next) replaces the configuration and reconciles at
once:
- A script with the same
id, source, inline code and attributes keeps its element. New callback functions alone do not reload it. - Changing the source, inline code or attributes starts a new load.
- A script that adds
onDisposeis tracked by object. Replacing the object disposes it and starts again, even with the sameid. Keep such objects stable. - A removed script releases its element. Shared external elements stay while another loader still uses them.
Updates requested from inside a callback run after the current pass; the latest wins. A chain of callbacks that keeps changing consent or the list is stopped after 100 passes, and the loader disposes itself.
loader.getLoadedScriptIds() lists the scripts currently loaded.
Attach the loader to your own kernel
A kernel from createConsentKernel has no loader. This file creates one with
persistence, before initialization:
createScriptLoader({ kernel, scripts, nonce?, onDebug? }) returns the
handle. onDebug receives every lifecycle step, for logging. Dispose the
loader before the kernel. Do not attach a loader to a kernel that
@c15t/browser or createConsentRuntime owns; each already has one.
Check it works
- Filter the Network tab for the vendor's host. Nothing loads before a choice.
- Allow the category. The vendor loads and
onLoadruns. In the Elements panel, the script sits in<head>with a randomid. - With
reloadOnConsentRevoked: false, withdraw the category.onConsentChangeruns withhasConsent: falseand the element is gone.