> ## Documentation Index
> Fetch the complete documentation index at: https://dokumentation.websale.de/llms.txt
> Use this file to discover all available pages before exploring further.

# $wsCookie - Browser cookies

> Reference for $wsCookie: read, set, update and delete browser cookies in templates for preferences, dark mode and onsite personalization.

With the `$wsCookie` module, you read, set, update, and delete [browser cookies](/en/glossar#cookie) directly in the template.

Cookies store information per visitor across multiple page views, for example a chosen display variant, a personal greeting, or a remembered consent. This page covers only the reading and writing of cookies themselves. How you check the user's [consent](/en/glossar#consent) is described in [\$wsConsent](/en/frontend/referenz/module/wsconsent).

<Note>
  Note: Setting cookies may require user consent depending on the type (technically necessary vs. marketing/tracking). Use [\$wsConsent.checkAllowed()](/en/frontend/referenz/module/wsconsent#\$wsconsent-checkallowed) to verify consent before setting non-essential cookies.
</Note>

***

## Basic concept

Cookies always follow the same flow: **Set → Read → React.** \
Before you can read a cookie, it must first be set.

A cookie is always set by an event. Typical triggers are:

* a user action (clicking a toggle or link),
* a state (the customer logs in),
* a dependency (a specific customer data field is present),
* the entry path (access via a [campaign link](/en/glossar#kampagnen-link) or [referrer](/en/glossar#referrer)).

On the next page view, you read the value back and react to it, for example with a different display or a personal greeting.

Important regarding the time of execution: \
The [template code](/en/glossar#template-code) is executed when the page is built, not when the user clicks. A cookie is therefore not set at the moment of the click, but only when the click triggers a new request and the template is executed again. Always plan the setting along a page view.

### Naming scheme (applies to all methods)

The shop automatically renames its own cookies according to the scheme `wsvx_<shopId>_cookie<name>`. `setCookie("test", …)` in the shop `example` becomes the cookie `wsvx_example_cookietest` in the browser. In the methods, you always work only with the short name (`test`); the shop adds the prefix itself. This avoids conflicts with other cookies.

Setting and reading use the same scheme. A cookie set with `setCookie("test", …)` can therefore be read again directly with `getCookie("test")`, without worrying about the long name.

**Exception: externally set cookies**\
Cookies that were not set by the shop (e.g. via custom JavaScript, a third-party tool, or a cookie banner) keep their original name. The automatic scheme would not match them. If you want to read such a cookie, pass the `keepNameAsIs: true` option to [getCookie](/en/frontend/referenz/module/wscookies#\$wscookie-getcookie) so that the name is used unchanged. This option is not available for setting, updating, or deleting, because external cookies are usually managed by the tool that set them.

***

## Module overview

**Example / excerpt of** `$wsCookie`

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{= $wsCookie | json }}
```

**JSON output**

```json theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{
  "deleteCookie": "ƒ()",
  "getCookie": "ƒ()",
  "setCookie": "ƒ()",
  "updateCookie": "ƒ()"
}
```

Note: `"ƒ()"` indicates a function.

**Methods at a glance**

| **Method**       | **Return type** | **Description**                          |
| ---------------- | --------------- | ---------------------------------------- |
| `getCookie()`    | string          | Reads the value of a cookie by its name. |
| `setCookie()`    | –               | Sets a new cookie.                       |
| `updateCookie()` | string          | Updates the value of an existing cookie. |
| `deleteCookie()` | –               | Deletes a cookie by its name.            |

***

## Templates

All four methods are template functions and are available in every `.htm` template. The typical flow (Set → Read → React) is described in the [Basic concept](#basic-concept) section.

***

## Variables

No variables are available for `$wsCookie`.

***

## Methods

### \$wsCookie.getCookie()

Reads the value of a cookie by its name.

**Signature**\
`$wsCookie.getCookie(name, [options])`

**Return**\
`string` – value of the cookie. The value is empty if no cookie with this name exists.

**Parameters**

| **Name**  | **Type** | **Required** | **Default** | **Description**                                                                                                                                                                        |
| --------- | -------- | ------------ | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`    | string   | yes          | –           | Short name of the cookie, without prefix.                                                                                                                                              |
| `options` | map      | no           | –           | `keepNameAsIs` (bool) – uses the name unchanged, without the automatic scheme. <br />Required for externally set cookies (see [Basic concept](#naming-scheme-applies-to-all-methods)). |

**Example** that reads a stored display variant.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ var $currentTheme = $wsCookie.getCookie("wsThemeColor") }}
```

### \$wsCookie.setCookie()

Sets a new cookie.

**Signature**\
`$wsCookie.setCookie(name, value, [age])`

**Return**\
– (no return value)

**Parameters**

| **Name** | **Type**                            | **Required** | **Default** | **Description**                                                                                                                                                                                        |
| -------- | ----------------------------------- | ------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `name`   | string                              | yes          | –           | Short name of the cookie, without prefix.                                                                                                                                                              |
| `value`  | string, int, float, list, map, bool | yes          | –           | Value of the cookie. Complex values (list, map) are stored internally as [JSON](/en/glossar#json) automatically.                                                                                       |
| `age`    | int                                 | no           | Session     | Validity period in seconds. Without a value, a [session cookie](/en/glossar#session-cookie) is set, which expires at the end of the browser session. Maximum 10 years. Example: `31536000` = 365 days. |

**Example** that sets a display variant.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ $wsCookie.setCookie("wsThemeColor", "dark") }}
```

**Example** that sets a cookie with a validity period of 365 days.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ $wsCookie.setCookie("welcomeBackName", $wsAccount.displayName, 31536000) }}
```

### \$wsCookie.updateCookie()

Updates the value of an existing cookie.

**Signature**\
`$wsCookie.updateCookie(name, value)`

**Return**\
`string` – new value of the cookie.

**Parameters**

| **Name** | **Type**                            | **Required** | **Default** | **Description**                           |
| -------- | ----------------------------------- | ------------ | ----------- | ----------------------------------------- |
| `name`   | string                              | yes          | –           | Short name of the cookie, without prefix. |
| `value`  | string, int, float, list, map, bool | yes          | –           | New value of the cookie.                  |

**Example** that updates a display variant.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ $wsCookie.updateCookie("wsThemeColor", "light") }}
```

### \$wsCookie.deleteCookie()

Deletes a cookie by its name. Internally, the lifetime is set to `-1`, which causes the browser to discard the cookie immediately.

**Signature**\
`$wsCookie.deleteCookie(name)`

**Return**\
– (no return value)

**Parameters**

| **Name** | **Type** | **Required** | **Default** | **Description**                           |
| -------- | -------- | ------------ | ----------- | ----------------------------------------- |
| `name`   | string   | yes          | –           | Short name of the cookie, without prefix. |

**Example** that deletes a previously set cookie.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ $wsCookie.deleteCookie("wsThemeColor") }}
```

***

## Actions

No actions are available for `$wsCookie`.

***

## Examples

The following examples are ordered from simple to more complex: from a state, through a user action and the entry path, to an externally set cookie, and a complete conversion tracking that combines an external cookie with a consent check.

### Personal greeting (WelcomeBack cookie)

The trigger here is the "login" state. When the customer opens the shop, they are greeted personally, even if they are not logged in, provided they have logged in at least once before. This preserves a personal touch without requiring the customer to stay logged in permanently.

**Step 1: Set the cookie on login**

As soon as the customer has logged in, their display name is stored in the cookie. If the display name is missing or empty, the email address is used instead. The third parameter specifies the validity period in seconds (here 365 days), so that the greeting is preserved over a longer period.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ if $wsAccount.isLoggedIn }}
  {{ var $welcomeName = $wsAccount.displayName }}
  {{ if not $welcomeName }}
    {{ $welcomeName = $wsAccount.email }}
  {{ /if }}
  {{ $wsCookie.setCookie("welcomeBackName", $welcomeName, 31536000) }}
{{ /if }}
```

**Step 2: Display the greeting on any page**

On any page, for example the home page, you read the name and display the corresponding greeting. If no cookie is present, because the customer has never logged in or the cookie has expired, nothing is displayed.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ var $name = $wsCookie.getCookie("welcomeBackName") }}
{{ if $name }}
  Welcome back, {{= $name }}!
{{ /if }}
```

**Result**\
Returning customers see their personal greeting until the cookie expires or is deleted.

<Tip>
  **Note:** This cookie contains personal data. Check consent via [\$wsConsent.checkAllowed()](/en/frontend/referenz/module/wsconsent#\$wsconsent-checkallowed) and refer to the cookie policy on the home page.
</Tip>

### Storing a display variant (e.g. dark mode)

The trigger here is a user action, namely a click. Since WEBSALE does not work with a theme system, you have to create a display variant yourself. To do this, you store the user's choice in a cookie and load the matching style sheet when the page is built.

**Step 1: Provide the trigger**

The user chooses the variant via a link. The click triggers a page view, and only then can the value be set (see [time of execution](#basic-concept)).

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<a href="?theme=dark">Enable dark mode</a>
<a href="?theme=light">Disable dark mode</a>
```

**Step 2: Write the choice to the cookie when the page is built**

The validity period of 365 days (in seconds) ensures that the choice is preserved beyond the session. Without this value, it would only be a session cookie and the setting would be lost after closing the browser.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ if $wsViews.current.params.theme }}
  {{ $wsCookie.setCookie("wsThemeColor", $wsViews.current.params.theme, 31536000) }}
{{ /if }}
```

**Step 3: Read on every page view and load the matching style sheet**

The cookie only stores the choice. The visible change is created by the matching [style sheet](/en/glossar#stylesheet). That's why there is a switch between the dark and light variant.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ var $currentTheme = $wsCookie.getCookie("wsThemeColor") }}
{{ if $currentTheme == "dark" }}
  <link rel="stylesheet" href="/css/theme-dark.css">
{{ else }}
  <link rel="stylesheet" href="/css/theme-light.css">
{{ /if }}
```

**Result**\
The chosen variant is preserved across all page views until the user switches it.

### Onsite personalization: audience-specific content

The trigger here is the entry path, i.e. a campaign link through which the visitor enters the shop. Unlike the personal greeting, this is true [onsite personalization](/en/glossar#onsite-personalisierung): you display different content on the shop pages depending on a characteristic of the visitor.

The scenario: A pet supplies shop sends separate newsletters to cat and dog owners. Via the link in the newsletter, the shop remembers the target group and from then on shows matching content.

**Step 1: Campaign link in the newsletter**

The links in the newsletter carry the target group as a parameter:

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<a href="https://your-shop.com/?kundengruppe=katze">To our offers for cats</a>
<a href="https://your-shop.com/?kundengruppe=hund">To our offers for dogs</a>
```

**Step 2: Write the target group to the cookie when the page is built**

When the visitor opens the shop via the link, the template reads the parameter and stores the target group. The validity period of 30 days (2592000 seconds) ensures that the assignment is preserved on later visits as well.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ if $wsViews.current.params.kundengruppe }}
  {{ $wsCookie.setCookie("kundengruppe", $wsViews.current.params.kundengruppe, 2592000) }}
{{ /if }}
```

**Step 3: Display the matching content on every page**

On the shop pages, you determine the target group and display the matching content. Important: A freshly set cookie can only be read on the **next** page view (see [time of execution](#basic-concept)). On the first call via the campaign link, you therefore fall back on the parameter and only afterwards on the cookie. This way, the visitor already sees the right content when entering. If neither parameter nor cookie is present, you display neutral default content.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ var $gruppe = $wsViews.current.params.kundengruppe }}
{{ if not $gruppe }}
  {{ $gruppe = $wsCookie.getCookie("kundengruppe") }}
{{ /if }}

{{ if $gruppe == "katze" }}
  <h2>Our bestsellers for cats</h2>
  {{# banners and products for cat owners go here #}}
{{ else }}
  {{ if $gruppe == "hund" }}
    <h2>Our bestsellers for dogs</h2>
    {{# banners and products for dog owners go here #}}
  {{ else }}
    <h2>For cat and dog lovers</h2>
    {{# neutral default content as long as no target group is known #}}
  {{ /if }}
{{ /if }}
```

**Result**\
Visitors from the cat newsletter see the cat content on every visit, visitors from the dog newsletter see the dog content. All others see the default content.

<Note>
  Note: The cookie value is readable in the browser and can be modified by the user. Therefore, use the target group only to display content, not for security-relevant decisions such as prices or access rights. Since this is not a technically necessary cookie, check the user's consent beforehand.
</Note>

### Reading an externally set cookie

The trigger here is a cookie that was set outside the shop, for example by your own JavaScript or by a third-party tool. Such cookies keep their original name and are not renamed automatically. To read them, pass `keepNameAsIs: true`. Otherwise, the shop will look for the renamed name and won't find the cookie.

In the example, a [flag](/en/glossar#flag) set via JavaScript indicates that an info popup has already been seen:

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{# The cookie was set via JavaScript:
    document.cookie = "infoPopupSeen=true"
    In the template we read it unchanged with keepNameAsIs. #}}
{{ var $popupSeen = $wsCookie.getCookie("infoPopupSeen", { keepNameAsIs: true }) }}
{{ if $popupSeen != "true" }}
  {{# Cookie not present, show info popup #}}
{{ /if }}
```

**Result**\
The popup only appears as long as the externally set flag is missing.

### Affiliate conversion tracking with an external cookie

The trigger here is an externally set cookie in combination with the order completion. Affiliate networks (e.g. Awin) set their own cookie with a click identifier when a partner link is clicked. Once the order is completed, the shop reports the conversion back to the network, passing along this click identifier so that the referral is attributed to the correct partner.

Two things are decisive for `$wsCookie` here: The network cookie was set outside the shop and is therefore read with `keepNameAsIs: true`. And because conversion tracking serves marketing purposes, it is only executed after the user has given consent. You check consent via [\$wsConsent](/en/frontend/referenz/module/wsconsent) – not via a separately read consent cookie.

**Step 1: Read the click identifier from the external cookie**

The network's cookie (in the example `awc`) keeps its original name, hence `keepNameAsIs: true`. If it is missing, the value remains empty.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ var $awcCookie = $wsCookie.getCookie("awc", { keepNameAsIs: true }) }}
```

**Step 2: Only track with marketing consent**

Instead of reading a foreign consent cookie, you check consent via `$wsConsent`. Only if marketing consent is present are the tracking call and the counting pixel emitted. The order data passed in (total, order number, voucher, currency) comes from the checkout.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{# Check marketing consent – see $wsConsent for the exact category/signature #}}
{{ if $wsConsent.checkAllowed("marketing") }}

  {{ var $eventData = join([
      "&merchant=00000",
      "&amount=", $wsCheckout.sum.totalNet,
      "&ch=aw",
      "&parts=DEFAULT:", $wsCheckout.sum.totalNet,
      "&vc=", ifnull($wsVoucher.vouchers[0].id, ""),
      "&cr=", $wsCheckout.sum.currency,
      "&ref=", $wsCheckout.orderId,
      "&testmode=0",
      "&cks=", ifnull($awcCookie, "")
    ], "") }}

  {{ $wsAsse.fire("dwintracking", $eventData) }}

  <script>
    document.addEventListener("DOMContentLoaded", function () {
      if (typeof AWIN != "undefined" && typeof AWIN.Tracking != "undefined") {
        AWIN.Tracking.Sale = {};
        AWIN.Tracking.Sale.amount   = parseFloat("{{= $wsCheckout.sum.totalNet }}").toFixed(2);
        AWIN.Tracking.Sale.channel  = "aw";
        AWIN.Tracking.Sale.orderRef = "{{= $wsCheckout.orderId }}";
        AWIN.Tracking.Sale.parts    = "DEFAULT:" + parseFloat("{{= $wsCheckout.sum.totalNet }}").toFixed(2);
        AWIN.Tracking.Sale.currency = "{{= $wsCheckout.sum.currency }}";
        AWIN.Tracking.Sale.voucher  = "{{= ifnull($wsVoucher.vouchers[0].id, '') }}";
        AWIN.Tracking.Sale.test     = "0";
        AWIN.Tracking.run();
      }
    });
  </script>

  <img border="0" height="0" width="0" style="display: none;"
       src="https://www.awin1.com/sread.img?tt=ns&tv=2&merchant=00000&amount={{= $wsCheckout.sum.totalNet }}&ch=aw&parts=DEFAULT:{{= $wsCheckout.sum.totalNet }}&ref={{= $wsCheckout.orderId }}&cr={{= $wsCheckout.sum.currency }}&vc={{= ifnull($wsVoucher.vouchers[0].id, '') }}&testmode=0">

{{ /if }}
```

**Result**\
If marketing consent is present, the conversion, including the external click identifier, is reported to the affiliate network. Without consent, nothing happens.

<Note>
  Note: The `merchant` value (`00000`), the `dwintracking` event, the cookie name `awc`, and the Awin-specific calls are network- or shop-specific and shown here only as an example. The part relevant for `$wsCookie` is reading the external cookie with `keepNameAsIs: true`; consent is checked via [\$wsConsent](/en/frontend/referenz/module/wsconsent), not via a separate consent cookie.
</Note>

***

## Further reading

* [\$wsConsent](/en/frontend/referenz/module/wsconsent) – check the user's consent before setting non-essential cookies.
