---
title: Wisepops
description: Load Wisepops popups, bars, embeds and notification feeds after
  marketing consent with c15t. Configure its site ID, cookie options and reload
  behavior.
seoTitle: Wisepops cookie consent with c15t
seoDescription: Load Wisepops after marketing consent, configure its site ID and
  cookie options, and use c15t's reload on consent revocation to stop the
  running SDK.
icon: megaphone
group: integrations
lastModified: "2026-10-26T22:16:35Z"
lastAuthor: Kaylee
---
The c15t `wisepops` helper loads Wisepops only after `marketing` has consent.
Before then, it creates no Wisepops snippet queue and writes no Wisepops
storage. It queues `defaultConsent: 'denied'` before the SDK runs and calls
`wisepops('consent', 'granted')` after the loader finishes with consent still
allowed. Keep c15t's default reload on revocation enabled because Wisepops
cannot revoke consent programmatically.

## Configure Wisepops

In Wisepops, open **Settings > Install Wisepops** and copy your site's setup
code. The loader URL contains your public site ID in its `h` parameter:
`https://wisepops.net/loader.js?v=3&h=YOUR_WISEPOPS_SITE_ID`.

Use the c15t helper in place of the Wisepops installation snippet. Remove
other Wisepops installations, including any tag manager or CMS plugin that
loads it, so they do not bypass c15t's consent check. Use one Wisepops site
ID per document. Reload the page when changing site IDs; replacing a helper
configuration does not reinitialize an SDK that has already started.

|Package manager|Command|
|:--|:--|
|npm|`npm install @c15t/integrations`|
|pnpm|`pnpm add @c15t/integrations`|
|yarn|`yarn add @c15t/integrations`|
|bun|`bun add @c15t/integrations`|

The `scripts` export below uses marketing consent. `experienceScripts` is an
alternative for campaigns whose purpose and policy call for experience
consent. Register one array.

```ts title="src/consent-scripts.ts"
import { wisepops } from '@c15t/integrations/wisepops';

export const scripts = [wisepops({ siteId: 'YOUR_WISEPOPS_SITE_ID' })];

export const experienceScripts = [
	wisepops({
		category: 'experience',
		options: { browserStorage: true },
		siteId: 'YOUR_WISEPOPS_SITE_ID',
	}),
];
```

## Register the scripts

Complete your [framework quickstart](/docs/frameworks) first. Keep its Inth
endpoint, policy, styles and consent UI. Remove the vendor's original script,
SDK initializer or tag-manager entry, so the vendor loads only through c15t.

The vendor pages put the helper in `src/consent-scripts.ts`. If your framework
quickstart already has a `scripts` array, such as the one in `c15t.config.ts`
in the Next.js guide, add the helper to that array instead of creating a
second file.
The `scripts` export is a configuration, not an initializer. Add it to the c15t provider you already have, at the registration
point for your framework below. These are edits to that provider, not a second
provider.

**Next.js**

Add the configuration to `scripts` in `c15t.config.ts`, next to
`next.config.ts`:

```ts title="c15t.config.ts"
import { defineConsentConfig } from 'c15t/next';
import { scripts } from './src/consent-scripts';

export default defineConsentConfig({ scripts });
```

Keep the rest of your config, such as `mode` and `routePrefix`, in the
same call. `ConsentRoot` reads the config in the browser, so the layout
keeps passing only `state`. App Router, Pages Router and static export all
read the same file. See
[Next.js scripts and embeds](/docs/frameworks/next/scripts).

**TanStack Start**

Import the configuration into your root route and pass it to the existing
`ConsentRoot` as a top-level prop. Keep the loader from the
[TanStack Start quickstart](/docs/frameworks/tanstack-start/quickstart):

```tsx title="src/routes/__root.tsx"
import { scripts } from '../consent-scripts';

<ConsentRoot state={consent} scripts={scripts}>
```

Import vendor helpers in the root route module, not in a server function.
A server function's return value must be serializable, and script
configurations carry callbacks. See
[TanStack Start scripts](/docs/frameworks/tanstack-start/scripts).

**React**

Add the configuration to the existing `ConsentProvider` options, next to
`mode`:

```tsx title="src/consent.tsx"
import { scripts } from './consent-scripts';

<ConsentProvider options={{ mode, scripts }}>
```

`mode` is the `manifest()` value from the
[React quickstart](/docs/frameworks/react/quickstart). Keep the banner,
dialog and preferences link inside the provider. See
[React scripts and embeds](/docs/frameworks/react/scripts).

**Nuxt**

Register the scripts under the `c15t` key in `app/app.config.ts`. Adjust the
relative import to where you created `consent-scripts.ts`:

```ts title="app/app.config.ts"
import { scripts } from '../src/consent-scripts';

export default defineAppConfig({
  c15t: { scripts },
});
```

The Nuxt module merges this over its options in `nuxt.config.ts` and starts
one script loader in the browser after hydration, once it has applied the
visitor's stored choice and privacy signals. Keep `scripts` out of
`nuxt.config.ts`, which reaches the browser as JSON and drops the vendor
callbacks. Write the vendor IDs into `consent-scripts.ts`. See
[Nuxt scripts and embeds](/docs/frameworks/nuxt/scripts).

**Vue**

Pass the scripts to the existing `c15tVue` plugin call in `src/main.ts`:

```ts title="src/main.ts"
import { c15tVue, manifest } from 'c15t/vue/vue-plugin';
import { scripts } from './consent-scripts';

app.use(c15tVue, {
  mode: manifest(),
  scripts,
});
```

Keep your existing `mode` and other options. The plugin starts one
script loader when the app mounts, after it has applied the visitor's stored
choice. Do not also call `createScriptLoader` from a component. See
[Vue scripts and embeds](/docs/frameworks/vue/scripts).

**Astro**

Add the scripts to the client entrypoint from the
[Astro quickstart](/docs/frameworks/astro/quickstart), `src/c15t.client.ts`,
which the integration finds on its own. Keep `astro.config.mjs` as it is.
If the module already exports scripts, combine the two arrays.

```ts title="src/c15t.client.ts"
import type { C15tClientOptionsExtension } from 'c15t/astro';
import { scripts } from './consent-scripts';

export default { scripts } satisfies C15tClientOptionsExtension;
```

Vendor helpers contain callbacks, and the integration options in
`astro.config.mjs` are serialized into the page, so do not put helpers in
the integration's `scripts` option. The integration passes the client
entrypoint to the one runtime every page shares, including across
`ClientRouter` navigation.

**Svelte**

Import the scripts in the component that owns your existing provider and
pass them as a top-level prop:

```svelte title="src/App.svelte"
<script lang="ts">
  import { ConsentProvider, manifest } from 'c15t/svelte';
  import { scripts } from './consent-scripts';
</script>

<ConsentProvider mode={manifest()} {scripts}>
  <!-- Keep your application, consent UI and preferences link here. -->
</ConsentProvider>
```

Retain the styles and consent UI from the [Svelte quickstart](/docs/frameworks/svelte/quickstart).
The provider owns the loader and disposes it on unmount.

**SvelteKit**

Add the scripts to the existing `ConsentRoot` in the root layout. Keep the
handle and the layout load from the [SvelteKit quickstart](/docs/frameworks/sveltekit/quickstart).

```svelte title="src/routes/+layout.svelte"
<script lang="ts">
  import { ConsentRoot } from 'c15t/svelte';
  import { scripts } from '../consent-scripts';

  let { children, data } = $props();
</script>

<ConsentRoot state={data.consent} {scripts}>
  {@render children()}
  <!-- Keep your consent UI and preferences link here. -->
</ConsentRoot>
```

Import vendor helpers in the layout component, not in `+layout.server.ts`:
a server load cannot send functions to the browser. Prerendered, static and
SPA-mode pages use the same `scripts` prop. If you pass an externally owned
`runtime` to the provider, register scripts when creating that runtime instead.

**HTML**

The helpers in `@c15t/integrations` are ES modules that need a bundler. On a
page that loads the c15t script tag, paste the vendor's own snippet instead
and keep it inert until its category is allowed:

```html
<script type="text/plain" data-c15t-category="measurement">
  // The vendor's snippet, unchanged
</script>
```

Use the category this guide names for the vendor. c15t runs the snippet
once that category is allowed, and reloads the page when the visitor
withdraws it. Helper options on this page, such as `loadMode`, do not apply
to a pasted snippet. See [HTML scripts](/docs/frameworks/html/scripts).

**JavaScript**

Pass the scripts to `init()` from `@c15t/browser`, next to your mode:

```ts
import { init, manifest } from '@c15t/browser';
import { scripts } from './consent-scripts';

const consent = init({
  mode: manifest(),
  scripts,
});
```

Keep the mode from your quickstart. With
`createConsentRuntime` from `c15t/runtime`, pass `scripts` to it instead.
A kernel you create yourself needs a loader from
`c15t/modules/script-loader`. Attach one loader per kernel. See
[JavaScript scripts](/docs/frameworks/javascript/scripts).

To use the experience alternative, change the registration example's import
to `import { experienceScripts as scripts } from './consent-scripts'`, adjusting
the relative path for your framework. It also enables Wisepops localStorage.

## Options

|Option|Default|Behavior|
|--|--|--|
|`siteId`|Required|Public site ID from the setup snippet's `h` parameter. Trimmed and URL-encoded. A missing or blank value logs an error and skips the loader.|
|`category`|`'marketing'`|Consent category or condition required before any Wisepops setup. Use `'experience'` if it matches your campaign's purpose and policy.|
|`options.autoPageview`|Wisepops v3 default, `'url'`|`'url'` checks for campaigns after URL changes. `'init'` checks only after initialization. `false` disables automatic pageviews.|
|`options.browserStorage`|Omitted; new visitors use cookies|`true` stores visitor data in localStorage instead of cookies and migrates existing cookies. The data is then separate for each origin and cannot be shared across subdomains. Wisepops can reuse localStorage selected on an earlier visit.|
|`options.cookieMaxSize`|Wisepops keeps each cookie under 3 KB|Cookie size limit in bytes. Wisepops removes older visitor history when it reaches the limit. For example, `1024` sets a 1 KB limit.|
|`nonce`|None|CSP nonce for the c15t-managed loader. A nonce from your consent provider also applies. This does not set a nonce on resources created by Wisepops.|

The helper always sets `defaultConsent: 'denied'`; it is not a configurable
option. The helper grants Wisepops cookie consent only after c15t permits
the mapped category and the vendor. It has no `alwaysLoad` mode.

The helper's `options` are bootstrap configuration, queued before its loader
runs. Changing the configuration object does not update an SDK already
running on the page. Set these values before registering the scripts.

### Single-page applications

The helper uses Wisepops' v3 loader, whose default `autoPageview: 'url'`
detects URL changes. With `autoPageview: false`, call the browser SDK through
`window.wisepops?.('pageview')` after your router updates the URL, only while
the mapped category and Wisepops vendor are allowed. Recheck permission on
every manual call. The snippet queue can remain after revocation, so the
existence of `window.wisepops` alone is not a consent check.

Avoid sending manual pageviews alongside automatic ones. Duplicate pageviews
can cause campaigns to appear and disappear immediately. `autoPageview: false`
does not stop an SDK that has already started.

## Loading and revocation

Once the mapped consent allows Wisepops, c15t creates `window.wisepops` with
its snippet queue and timestamp, queues the runtime options and appends the
loader to `<head>`. After the SDK loads, c15t grants Wisepops cookie consent.
If c15t removes a downloading loader after revocation, the helper ignores
its later load event and sends no consent grant.

Wisepops' own denied mode still displays campaigns and uses session-only
cookies. This helper waits for c15t consent before loading Wisepops at all.
Do not install Wisepops separately to use its denied mode alongside this
helper.

On revocation, c15t removes the loader and reloads the page by default. The
new page does not load Wisepops while the mapped consent is denied. The
helper does not call `wisepops('consent', 'denied')`: Wisepops documents that
this call produces an error. The reviewed Wisepops documentation provides no
shutdown or destroy call for the helper to use.

Keep `reloadOnConsentRevoked` enabled. Setting it to `false` leaves the
already-running SDK, displayed campaigns, timers and listeners active.
Removing a script element also cannot guarantee that an in-flight download
will never execute. A reload ends the document; it does not erase data
already stored by Wisepops.

## Cookies and browser storage

Wisepops documents the following first-party cookies on your site's domain.
Actual values and lifetimes can vary with its configuration and the browser:

|Cookie|Documented lifetime|
|--|--|
|`wisepops_session`|Session|
|`wisepops_visitor`|750 days|
|`wisepops_noshow`|2 years|
|`wisepops_props`|2 years|
|`wisepops_visits`|2 years|
|`wisepops_tld`|Session|
|`wisepops`|2 years|

With `options.browserStorage: true`, Wisepops moves visitor data to
localStorage. c15t does not automatically delete Wisepops cookies or
localStorage on revocation. Your cleanup can remove only browser-accessible
data on domains, paths and origins your application can reach. It cannot
clear HttpOnly cookies or vendor-side records. Wisepops can remember an earlier
consent grant in its stored state, so reloading with `defaultConsent: 'denied'`
alone does not reset that grant. After c15t revocation, keep Wisepops blocked
on the new page.

Wisepops automatically integrates with Shopify Customer Privacy. Check
Wisepops' [Shopify consent behavior](https://support.wisepops.com/en/articles/12679556-cookie-consent-mode)
and your Shopify privacy settings separately. c15t's Wisepops helper does not
synchronize c15t choices with Shopify. Contact Wisepops support if you need to
disable its Shopify privacy integration, and use a single installation so a
Shopify app does not load the vendor outside c15t.

## Verify Wisepops

Use a private window with an opt-in policy. Open DevTools Network, disable
the cache and filter by `wisepops`. Check Cookies, Local Storage and Session
Storage in the Application panel too.

1. Load the page before choosing. There should be no Wisepops request, no
   `window.wisepops` snippet queue and no new Wisepops storage entry.
2. Reject consent and reload. Wisepops should stay blocked. Then allow an
   unrelated category, such as `measurement`, while the mapped category
   remains denied. Repeat the request, global and storage checks.
3. Allow the mapped category. Expect `loader.js?v=3&h=...` to load without a
   page reload. The query URL can redirect to a URL under
   `wisepops.net/h/<siteId>/`. Configured campaigns become eligible, but
   targeting and publishing settings can still prevent display.
4. Navigate within your app and check that Wisepops detects URL changes with
   automatic pageviews enabled. For manual pageviews, also test navigation
   while the category or vendor is denied.
5. Withdraw the mapped consent and save. Confirm that c15t reloads the page.
   The new document should make no Wisepops request and have no snippet queue.
   Previously stored data can remain; check deletion separately if you
   configured cleanup.
6. Test the Wisepops vendor switch inside an allowed category. Turning the
   vendor off should reload the page and keep Wisepops blocked even though the
   category remains allowed. Restore the mapped category and vendor permission
   to check that Wisepops can load again.

Keep `reloadOnConsentRevoked` enabled for these checks. Wisepops has no
documented opt-out API that a callback-only script could call in place of the
reload.

Sources: [Add Wisepops to a custom website](https://support.wisepops.com/en/articles/9975472-how-to-add-wisepops-to-a-custom-website),
[public installation snippet](https://wisepops.com/),
[Cookie Consent Mode](https://support.wisepops.com/en/articles/12679556-cookie-consent-mode),
[data collection and cookies](https://support.wisepops.com/en/articles/9898232-data-collection-cookies),
[browser storage and cookie size options](https://support.wisepops.com/en/articles/9898261-limit-the-impact-of-wisepops-cookies-on-your-website),
and [single-page applications](https://support.wisepops.com/en/articles/9897249-single-page-applications-spa).

Test in a private window with an opt-in policy. Open DevTools Network, disable
the cache and filter by the vendor's domain:

1. Load the page. No request goes to the vendor before you choose.
2. Click Reject, then reload. There is still no vendor request.
3. Open Privacy settings and allow the helper's category. The vendor script
   loads without a page reload.
4. Turn the category off again and save. c15t reloads the page, and the new
   page makes no vendor request.

c15t reloads on revocation because removing a script element does not stop
code that already ran. The vendor's listeners, timers and queued events stay
alive until the page unloads. If you set `reloadOnConsentRevoked: false`, stop
the vendor yourself. Register a callback-only script whose `onConsentChange`
calls the vendor's opt-out API, as shown in
[custom integrations](/docs/integrations/building-integrations), and check the
permission before each of your own event calls. The reload does not delete
cookies the vendor already set; see
[clear on revocation for your framework](/docs/integrations/overview#vendor-switches-and-cookie-cleanup).

The helper lists its vendor by name in the preference dialog, so a visitor can
turn it off inside an allowed category. See
[vendor consent for your framework](/docs/integrations/overview#vendor-switches-and-cookie-cleanup). The
[consent verification guide](/docs/guides/verify-consent) covers navigation,
expiry and hosting checks.
