Skip to content

The banner runtime and the consent cookie

Understand banner startup, the consent cookie, and the browser API.

banner.js installs script blocking, loads your product’s consent settings and exposes window.LepstoConsent to your page. It stores the visitor’s choice in lepsto_consent and applies that choice on later visits.

Load it synchronously before trackers. The runtime cannot block a tracker that has already executed.

Three fixed things about the snippet

Copy the snippet from consent_embed_get or Install on your site in the app. Replace <YOUR_PRODUCT_ID> below with your product id.

<!-- Lepsto consent: must be the first script in <head>. Do not add async or defer — trackers would run before consent is known. -->
<script src="https://public.lepsto.com/<YOUR_PRODUCT_ID>/consent/banner.js" data-product-id="<YOUR_PRODUCT_ID>"></script>

Keep three properties of this snippet:

  • Synchronous loading. Do not add async or defer; blocking must be installed before tracker scripts or inline loaders execute.
  • First script in <head>. Trackers parsed before the banner are outside its reach.
  • Matching product and URL. The runtime reads data-product-id, falling back to the UUID in src, and derives its base URL from that same element. This prevents accidentally duplicated tags from mixing one product’s id with another tag’s origin.

Keep the installation comment with the tag.

The bundle

GET https://public.lepsto.com/<YOUR_PRODUCT_ID>/consent/banner.js returns the shared runtime with a product-specific prelude containing:

  • Non-necessary provider hosts and their categories, used by the blocker.
  • The theme and customization, used for the banner’s first frame.

The response is cached for five minutes in the browser and any CDN. Its strong ETag is computed from the assembled bytes. Provider and theme changes alter it; revalidation returns 304 when the bytes are unchanged. An edit appears on the next request reaching the server, or within about five minutes as caches refresh.

If configuration cannot be read while serving the bundle, the server still returns it with the default theme.

What the runtime does in the page

Startup proceeds in this order:

  1. Install the blocker synchronously. See Script blocking.
  2. Set Google Consent Mode storage signals to denied, with wait_for_update.
  3. Install window.LepstoConsent.
  4. Read the stored choice. Apply a valid choice for a returning visitor, or show the banner when no valid choice exists.

The banner is isolated from your page

The runtime appends a <div> to <body> and renders the banner in an open shadow root. :host { all: initial } and explicit inherited-property overrides isolate it from page styles.

It checks that its stylesheet applied. If a policy such as strict style-src prevents that, it removes the host element and renders no banner. Check your browser console and policy if the UI does not appear.

Errors during blocker installation, startup, configuration fetching, Consent Mode updates, resource restoration or storage cleanup are caught and warned about once instead of being thrown into your page. Errors relevant to the visitor appear in the preferences panel.

The browser writes lepsto_consent after a decision. The server does not send it through Set-Cookie. It reads this name first, then the legacy lessly_consent cookie. If only the legacy cookie is present, the banner migrates its saved choice automatically to lepsto_consent.

Attribute
Value
Path
/
Max-Age
31536000 seconds (12 months)
SameSite
Lax
Secure
Enabled on https:
HttpOnly
No; the runtime must read it
Domain
Derived as described below

Its base64url-encoded JSON value contains the category flags, method, format version, timestamp, region, stamp, record id and provider-set versions used for the choice.

On a custom domain, the runtime probes candidate domains with a cookie and uses the first the browser accepts. The browser rejects public suffixes, leaving the registrable domain you own. Non-domain hosts and IP addresses use a host-only cookie. A derivation failure also falls back to host-only.

You can narrow the cookie domain. A cookie from another product on the same registrable domain is ignored, since its choice applies to that product’s providers.

The perimeter note

When the cookie covers a domain and its subdomains, the banner displays Your choice applies to this site and its subdomains. This notice has a fixed position and cannot be configured.

Returning visitors

A valid stored choice normally suppresses the banner. The runtime applies it to Consent Mode and shows the floating icon or connects your custom preferences trigger.

Adding a provider to an allowed category increments that category’s provider-set version. Returning visitors with an older version are asked again, with their earlier choice filled in. The old choice stays in force until they decide again. Moving a provider into a category also changes that category’s version.

Renaming, changing hosts or cookies, reordering and deleting providers do not prompt again. Visitors who denied every optional category are not re-prompted for additions because no allowed category has expanded. Cookie expiry still requires a new choice.

window.LepstoConsent

Member
Result
openPreferences()
Opens preferences
getDecision()
Returns a frozen snapshot of the current choice, or null
withdrawAll()
Withdraws all optional categories
onChange(fn)
Calls fn when the choice changes

Connect a settings button after the runtime is available:

<button onclick="window.LepstoConsent.openPreferences()">Cookie settings</button>

For analytics you start yourself, apply both the current choice and later updates. Supply your own startAnalytics and stopAnalytics implementations:

// startAnalytics and stopAnalytics are yours to write.
let started = false;

function applyDecision(decision) {
  const allowed = decision?.statistics === true;
  if (allowed && !started) {
    startAnalytics();
    started = true;
  } else if (!allowed && started) {
    stopAnalytics();
    started = false;
  }
}

applyDecision(window.LepstoConsent.getDecision());
window.LepstoConsent.onChange(applyDecision);

The initial call covers returning visitors. The subscription covers first-time decisions and later withdrawals. onChange can fire even when statistics has not changed; started avoids starting the same integration twice. Omit that flag only if your start and stop functions already handle repeated calls safely.

Before a choice, getDecision() returns null, which this example treats as not allowed. Later calls return new snapshots; an older snapshot does not update itself.

Calling it before the runtime installs

If code executes before the runtime is installed, initialize a queue:

<script>
  window.LepstoConsent = window.LepstoConsent || [];
  window.LepstoConsent.push(['openPreferences']);
</script>

window.LesslyConsent remains a legacy alias for the same runtime object; use window.LepstoConsent in new code. The runtime drains the queue on installation and accepts only the four members above. A queued getDecision cannot return a value to the caller; call it directly once the runtime is ready. After installation, use direct method calls instead of the queue.

API changes follow the same process as panel changes: create a linked record, rewrite the cookie, update Consent Mode, block withdrawn categories and attempt to clear their storage.

Newly denying a category reloads the page because previously executed trackers cannot be undone. If your app handles stopping them itself, disable that reload:

await window.LepstoConsent.withdrawAll({ reload: false });

Next steps

Was this page helpful?
Esc

Start typing to search the docs.

↑↓navigate↵select