HTML Components
Gated scripts
Make a script wait for consent
Change the vendor's <script> to type="text/plain" and name the category it
needs in data-c15t-category. Browsers do not run a text/plain script. Once
the category is allowed, c15t puts a normal copy of the tag in its place, and
the browser runs the copy.
The first tag loads a file, the second holds the vendor's snippet inline. Both forms work. Remove the original, active copy of each vendor tag so it cannot run on its own.
Attributes
| Attribute | Required | What it does |
|---|---|---|
type="text/plain" | Yes | Stops the browser running the script. A tag without it runs at once, whatever its category. |
data-c15t-category | Yes | One category name: necessary, functionality, experience, measurement or marketing. |
data-c15t-vendor | No | A vendor slug. The tag also waits while the visitor has switched that vendor off. See vendor consent. |
src | No | Load the script from a URL. Without it, c15t runs the tag's inline text. |
async | No | Load without waiting for earlier gated scripts, and let later ones start without waiting for this one. |
data-c15t-activated | Set by c15t | true once c15t has run the tag, invalid when its category name is unknown, untrusted when the page has a nonce and the tag lacks it. Do not set it yourself. |
necessary is always allowed, so a necessary tag runs as soon as c15t
starts. A category name c15t does not know logs a console warning, and the tag
never runs.
What c15t copies to the new tag
The replacement tag gets src, async, defer, crossorigin, integrity,
referrerpolicy, id, nonce, every other data-* attribute, and the
inline text. It gets data-c15t-activated="true" in place of type. Other
attributes, such as class or fetchpriority, are not copied.
The copied nonce keeps a nonce-based Content Security Policy working. A
defer attribute has no effect on a script added after the page parsed, so
ordering follows the rules below instead.
Load order
c15t runs allowed tags in page order:
- An external script without
asyncfinishes loading, or fails, before c15t runs the next tag. An inline snippet after a library tag can use that library. - An external script with
asyncstarts loading and c15t moves on at once. - Tags that become allowed later, after a visitor accepts, run in page order among themselves.
Google's GA4 snippet shows why this matters. Its first tag loads the library and its second calls it:
Leave async off the first tag so the library loads before the second tag
runs.
When c15t looks for tags
- When it starts, which runs every tag a returning visitor already allowed.
- After every change to permissions, such as an accept, a save, or a policy that resolves.
- When a new gated tag is added to the page, for example by a page builder widget. A new tag whose category is already allowed runs straight away.
Each tag runs at most once per page load.
Gated tags on a page with a nonce
When the c15t tag has a nonce, from its own nonce attribute or from
data-nonce, c15t runs only the gated tags that carry the same nonce:
c15t skips a gated tag without the nonce, logs a console warning and marks the
tag data-c15t-activated="untrusted". The tag's category is not added to the
preference dialog. c15t never copies the nonce onto a gated tag, so add it to
every gated tag, including tags your own code inserts later. This check stops
markup injected into your page from running as a trusted script once its
category is allowed. A page without a nonce runs every gated tag.
Content Security Policy
covers the rest of the policy.
The categories reach the dialog
c15t adds the category of every gated tag to the categories the preference dialog offers, as long as the policy covers it. A visitor can then allow a category because your page uses it.
What happens when a visitor withdraws permission
A script that ran cannot be stopped. When a visitor turns off a category they had allowed, c15t saves the choice and reloads the page, and the new page starts with the tag inert again. Scripts covers turning the reload off and cleaning up cookies.
When to use config scripts instead
A gated tag is the simplest form and works in any CMS field that accepts
HTML. Use a scripts entry in config instead when you need a callback after
the vendor loads or when consent changes. See
load a script with callbacks.
Check it works
Open the page in a private window with the Network tab open.
- Filter for the vendor's domain. Nothing loads before a choice.
- Allow the tag's category. The vendor's request appears, and in the Elements
panel the tag now has
data-c15t-activated="true"and notype. - Reload. The vendor loads at once, before you touch the banner.