# Cookie consent

> Build the cookie banner and the preferences panel in Webflow with smootify-consent, over Shopify's Customer Privacy API, and let visitors reopen it from a preferences button.

Status: Ready (Smootify 2.0)  
1.x page: https://docs.smootify.io/docs/analytics/cookie-consent  
URL: https://www.smootify.io/docs/features/cookie-consent  
Updated: 2026-10-10

In regions with privacy laws, such as the EU and the UK, visitors must agree before a site tracks them. Every site needs the banner in Webflow, even one that does not sell in Europe: it shows only to the visitors who have to answer, and stays hidden for everyone else. Smootify does not add one: you design it in Webflow, like any other part of the site. The visitor’s choice is saved with Shopify’s Customer Privacy API, so the checkout, Shopify’s analytics and the pixels Smootify loads all follow it.

> [!NOTE]
> Before you start, turn on consent collection in Shopify: in the Shopify admin, open **Settings › Customer privacy**, then **Cookie banner**, activate it and choose the regions where visitors must answer. Shopify decides where consent is needed from these settings, and your Webflow banner shows only in those regions, and only to visitors who have not answered yet. Shopify's own banner does not show on the Webflow site: only the one you build here.

## How it fits together

What Smootify needs:

- one `smootify-consent` element on every page. A visitor can arrive on any page, so put it in a Component that every page uses, such as the footer;
- inside it, the buttons that accept and decline, and a button that opens the preferences;
- a preferences panel, where the visitor chooses category by category;
- a preferences button, so the visitor can change their choice at any time.

Visitors must always be able to choose their preferences, not only accept or decline everything, so the panel and the button are part of every banner.

It works on any domain, your `.webflow.io` address included: Smootify asks Shopify for the visitor’s region on the first visit and keeps the answer on your domain.

What is up to you: the text, the layout and where it sits (a bar at the bottom, a box in a corner).

**One Component for all of it.** The simplest setup puts everything in one `smootify-consent`, in the footer Component: the preferences button, and the banner and the preferences panel in a popover. The button stays in the footer; the popover is fixed where you want the banner, for example at the bottom of the window, not next to the button: it opens by itself at the top of the page too.

```html
<smootify-consent>
  <button data-is="preferences-button" popovertarget="consent">Cookie preferences</button>
  <div id="consent" popover>
    <div consent-panel="banner">…</div>
    <div consent-panel="preferences">…</div>
  </div>
</smootify-consent>
```

Without a preferences button inside it, Smootify shows the element only while the visitor has to answer, or while the preferences panel is open, and hides it the rest of the time. With the button inside, the element stays in the page with only the button wherever consent applies: keep the banner’s text and buttons inside the popover or a `consent-panel`, since everything else in the element stays visible with the button. Smootify adds no other style: position the banner yourself. In the Designer it is always visible.

## 1. Build the banner

Add a Custom element with the tag `smootify-consent`. Put your text and the buttons inside it. Each button is a Webflow Button, or a Custom element with the tag `button`, with the custom attribute `data-action`:

- `data-action="accept"`: accepts every category and closes the banner.
- `data-action="decline"`: declines every category and closes the banner.
- `data-action="preferences"`: opens the preferences panel (step 2).
- `data-action="close"`: hides the banner for this page without an answer. Nothing that needs consent loads, and the banner shows again on the next page.

```html
<smootify-consent>
  <div>We use cookies to measure the site and to show relevant ads.</div>
  <a href="#" class="w-button" data-action="accept">Accept all</a>
  <a href="#" class="w-button" data-action="decline">Decline</a>
  <a href="#" class="w-button" data-action="preferences">Preferences</a>
</smootify-consent>
```

## 2. Add the preferences panel

The preferences panel lets visitors choose category by category. Every banner needs it. Inside `smootify-consent`, make two Div blocks:

- one with `consent-panel="banner"`, holding the first message and its buttons, including `data-action="preferences"`. It hides while the preferences are open;
- one with `consent-panel="preferences"`. It shows only while the preferences are open.

Webflow keeps Checkboxes inside a Form Block, so put a Form Block in the preferences panel. In each Checkbox’s settings, set its Name to one of these:

| Checkbox Name | Checked, the visitor allows | Without its Checkbox, Save… |
|---|---|---|
| `analytics` | measuring the site, such as Google Analytics and Shopify's reports | declines it |
| `marketing` | advertising, such as the Meta, Google Ads and TikTok pixels | declines it |
| `preferences` | remembering their settings on the site | declines it |
| `sale_of_data` | the sale or sharing of their data, under US privacy laws. It only matters in some regions, such as some US states: put its Checkbox inside an element with `consent-condition="sale-of-data"`, which shows only there | leaves it as it was |

Then add a button with `data-action="save"`. The Form Block only holds the Checkboxes: the form is never sent.

```html
<div consent-panel="preferences">
  <div class="w-form">
    <form>
      <label class="w-checkbox"><input type="checkbox" name="analytics" class="w-checkbox-input"><span class="w-form-label">Analytics</span></label>
      <label class="w-checkbox"><input type="checkbox" name="marketing" class="w-checkbox-input"><span class="w-form-label">Marketing</span></label>
      <label class="w-checkbox"><input type="checkbox" name="preferences" class="w-checkbox-input"><span class="w-form-label">Preferences</span></label>
      <div consent-condition="sale-of-data"><label class="w-checkbox"><input type="checkbox" name="sale_of_data" class="w-checkbox-input"><span class="w-form-label">Allow the sale of my data</span></label></div>
      <a href="#" class="w-button" data-action="save">Save my choices</a>
    </form>
    <div class="w-form-done"><div>Saved</div></div>
    <div class="w-form-fail"><div>Something went wrong</div></div>
  </div>
</div>
```

- **Checked means yes.** When the panel opens, each Checkbox shows the visitor’s current choice. Write every label so that checked means “allowed”: “Allow the sale of my data”, not “Do not sell my data”.
- **Save** saves the checked boxes and closes the banner. Add a Checkbox for each of `analytics`, `marketing` and `preferences`: a missing one counts as declined.

## 3. Let visitors change their mind

Visitors must be able to change their choice later, as easily as they made it. Add a Custom element with the tag `button` and the custom attribute `data-is="preferences-button"`, inside `smootify-consent` as above or anywhere else, for example in the footer. A click opens the preferences panel.

```html
<button data-is="preferences-button">Cookie preferences</button>
```

- **It needs the banner.** Without a `smootify-consent` on the same page, Smootify removes the button.
- **It needs the button tag.** A Webflow Button publishes a link, and does nothing here.
- **It hides while the banner is on screen**, and shows again once the visitor has answered.
- **It shows only where consent applies:** where Shopify asks for consent, where the visitor has already answered, and in regions with the sale-of-data choice, such as California, where it is the “Do not sell or share” link those laws ask for. Elsewhere it is hidden, and so is a `smootify-consent` that holds it, without leaving a gap. It also stays hidden until Shopify has answered. Smootify marks the button and the element with `is-applicable` when it applies.

## Style and placement

The banner goes through these states. Style them with the classes, as combo classes:

| State | Classes | The banner and the preferences button |
|---|---|---|
| The visitor has to answer | `is-required` on `smootify-consent`, `cookie-banner-visible` on the body | the banner shows; the preferences button is hidden |
| The preferences are open | `is-open` on `smootify-consent`, `cookie-banner-visible` on the body | `consent-panel="banner"` hides, `consent-panel="preferences"` shows |
| The visitor has answered | none | the banner is hidden; the preferences button shows and opens the preferences |
| No consent needed in the region | none | the banner never shows; the preferences button shows and opens the preferences, for example to refuse the sale of data |

- **More classes.** `smootify-consent` also gets `is-sale-of-data-region` in regions with the sale-of-data choice, and `is-applicable` where consent applies. Use `cookie-banner-visible` on the body, for example, to move a chat button up while the banner shows. See [state classes](https://www.smootify.io/docs/reference/state-classes).
- **In a popover.** A popover inside `smootify-consent`, made with Webflow’s Popover element or a Div block with the `popover` attribute, opens and closes with the banner. Escape or a click outside closes it and hides the banner for this page, without an answer. See [Popovers](https://www.smootify.io/docs/build/popovers).
- **The market suggestion** waits until the banner is closed before it shows: see [Market suggestion](https://www.smootify.io/docs/features/market-suggestion).

The full list of attributes is in the Reference: [consent attributes](https://www.smootify.io/docs/reference/attributes/consent) and [`smootify-consent`](https://www.smootify.io/docs/reference/elements/smootify-consent).

## What follows the choice

- **The pixels Smootify loads**: the Meta Pixel, Google Analytics 4, Google Ads and the TikTok Pixel, each with the category it needs. See [Analytics and pixels](https://www.smootify.io/docs/features/analytics).
- **Shopify’s analytics**: page views and additions to the cart reach your store’s reports once the visitor allows analytics. Visits and returning visitors need the analytics proxy: see below.
- **Webflow Analyze** follows the visitor’s choice.
- **The checkout** receives the same choice from Shopify.
- **Your own scripts**: a developer reads the choice with the [JavaScript API](https://www.smootify.io/docs/developers/javascript-api) and the [`consent_changed`](https://www.smootify.io/docs/reference/api/events/consent-changed) event.

### The analytics proxy

Shopify no longer reads the `shopify_y` and `shopify_s` cookies that a site sets in the browser. It now gives the visitor and session tokens from its own server, in a response header, and a site on another domain gets them through a small proxy. Without it, Shopify’s reports cannot tell a new visit from a returning visitor. Smootify calls the proxy once per visit, only after the visitor allows analytics, and never on your `.webflow.io` address.

- **Server plan:** Smootify’s own proxy does it. There is nothing to set up.
- **Free and Lite plans:** deploy the free proxy on your own Cloudflare account, with one click.
3. Open [shopify-headless-proxy](https://github.com/smootify/shopify-headless-proxy) and click **Deploy to Cloudflare**. Log in to Cloudflare and confirm. You get an address such as `https://shopify-headless-proxy.your-account.workers.dev`.
4. In Webflow, go to **Site settings › Custom code** and paste this in **Head code**, before the Smootify loader, with your address followed by `/_cks`:

```html
<script>
  window.smootifyCksEndpoint = "https://shopify-headless-proxy.your-account.workers.dev/_cks";
</script>
```

The proxy needs no domain or DNS change, and one call per visit stays well within Cloudflare’s free plan. To accept calls only from your site, set its `ALLOWED_ORIGIN` variable to your domain, such as `https://www.example.com`, in the Cloudflare dashboard.

The same proxy can also open the panel of Shopify’s chat, if you add it: see [Shopify Inbox chat](https://www.smootify.io/docs/features/shopify-inbox).

## Before you publish

On your `.webflow.io` address, turn on Smootify’s debugger with Alt+Shift+D (Option+Shift+D on a Mac). It points out analytics ids set on a page without `smootify-consent`, and CSS that still uses the 1.x body class. See [The debugger](https://www.smootify.io/docs/start/debugger).

> [!STUCK]
> - **The banner always shows, even after an answer.** Smootify's stylesheet is missing, and it is the stylesheet that hides the banner. Add `data-css="true"` to the loader: see [Install Smootify 2.0](https://www.smootify.io/docs/start/install).
> - **The banner never shows.** Consent collection is off in Shopify (**Settings › Customer privacy › Cookie banner**), so Shopify asks for consent nowhere. Or the store does not ask for it in the visitor's region, the visitor already answered, or your own styles hide the element. Smootify only hides it: it never shows what your styles hide.
> - **The preferences button disappears.** The page has no `smootify-consent`: put the banner in a Component that every page uses. Or consent does not apply in the visitor's region, and the button is hidden on purpose.
> - **The preferences button does nothing.** It is a Webflow Button, which is a link. Use a Custom element with the tag `button`.
> - **Save declines a category the visitor never saw.** A category without its Checkbox counts as declined. Add a Checkbox for each of `analytics`, `marketing` and `preferences`.
> - **The banner comes back on the next page.** The visitor closed it with `data-action="close"`, or closed its popover from outside. That hides it for one page, without an answer.

## Coming from 1.x

- 2.0 no longer loads Shopify’s cookie banner. Its appearance settings in Shopify, the `#shopify-pc__…` selectors and its CSS variables no longer apply. A site that relied on that banner has none until you add `smootify-consent`.
- The body class `is-shopify-cookie-banner-visible` is now `cookie-banner-visible`: rename it in your styles.
- `button[data-is="preferences-button"]` keeps working, and opens your new banner.
