# Subscription widget

The **Subscription widget** is a launcher bell plus a prompt that invites website visitors to subscribe to push notifications. It replaces the [push subscription button](/developer/pushwoosh-sdk/web-push-notifications/push-subscription-button/), the [Custom Subscription Popup](/developer/pushwoosh-sdk/web-push-notifications/custom-subscription-popup/), and the [Subscription Prompt](/developer/guides/messaging-channels/subscription-prompt/) widgets in one component. While it is enabled, none of those three load on the page, so visitors never see two competing asks.

A visitor who has not decided yet sees the ask. A visitor who already blocked notifications sees an explanation of how to unblock them instead.

## Prerequisites

Make sure you've implemented Pushwoosh Web SDK 3.0 on your website. To do so, follow our [integration guide](/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#integration).

## 1. Enable the widget

Add the `subscriptionWidget` parameter to the WebSDK initialization and set `enable` to `true`:

```javascript
Pushwoosh.push(['init', {
  //...
  subscriptionWidget: {
    enable: true,
  }
}]);
```

Every field below is optional. An omitted field falls back to its default. In the init snippet, a number outside its range is silently clamped to the nearest bound and an invalid enum value silently falls back to its default, with no console warning. The Control Panel form described further down rejects an invalid value outright and refuses to save it.

## 2. Configure the prompt

```javascript
Pushwoosh.push(['init', {
  //...
  subscriptionWidget: {
    enable: true,
    prompt: {
      layout: 'card',       // 'card' | 'bar' | 'modal'
      position: 'bottom-right',
      iconUrl: 'https://url-image.png',
      borderRadius: 12,     // 0–32
      overlay: false,
    },
    launcher: {
      enabled: true,
      position: 'bottom-right', // 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
      offset: 20,           // 0–100
      size: 48,             // 32–80
    },
    display: {
      trigger: 'auto',      // 'auto' | 'launcher' | 'manual'
      delay: 5,             // seconds, 0–3600
      cappingCount: 3,      // auto-shows per visitor, 0–100, 0 = unlimited
      cappingInterval: 24,  // hours between auto-shows, 0–8760
    },
    texts: {
      title: 'Stay in the loop',
      message: 'Get notified about news and updates.',
      acceptText: 'Allow',
      declineText: 'Not now',
      closeText: 'Close',
      blockedTitle: 'Notifications are blocked',
      blockedMessage: 'Enable notifications in your browser settings to stay updated.',
      launcherLabel: 'Subscribe to notifications',
      launcherBlockedLabel: 'Notifications are blocked',
    },
    colors: {
      background: '#ffffff',
      accent: '#4285f4',
      text: '#000000',
      textSecondary: '#666666',
    },
  }
}]);
```

### Layout and position

`prompt.layout` sets the shape of the prompt:

- **`card`** — a compact card anchored to an edge or a corner. `prompt.position` accepts `top`, `bottom`, `top-left`, `top-right`, `bottom-left`, or `bottom-right`.
- **`bar`** — a bar spanning the full width of the page. `prompt.position` accepts `top` or `bottom` only.
- **`modal`** — a window centered on the page. `prompt.position` is always `center`.

Check the list above for the layout you set before picking a position. In the init snippet, a position the current layout doesn't support is silently replaced with the layout's default. The Control Panel form rejects the combination instead.

### When to show it

`display.trigger` controls what opens the prompt:

- **`auto`** — opens automatically `display.delay` seconds after the page loads, then again every `display.cappingInterval` hours if the visitor hasn't decided, up to `display.cappingCount` times total (`0` means no limit).
- **`launcher`** — opens only when the visitor clicks the bell.
- **`manual`** — opens only through `Pushwoosh.moduleRegistry.subscriptionWidget`, described below.

`display.delay`, `display.cappingCount`, and `display.cappingInterval` only apply to the `auto` trigger.

### The launcher bell

`launcher.enabled` shows a floating bell while the visitor is not subscribed, independent of `display.trigger` — even with `trigger: 'auto'`, the bell stays visible so the visitor can reopen the prompt after closing it. `launcher.position` places it in one of the four corners. `launcher.offset` (pixels from the edges) and `launcher.size` (the bell's diameter in pixels) fine-tune the placement.

### Texts and colors

`texts.blockedTitle`, `texts.blockedMessage`, and `texts.closeText` are shown instead of the ask once the browser reports the visitor blocked notifications — `texts.acceptText` and `texts.declineText` never appear in that state. `texts.launcherLabel` and `texts.launcherBlockedLabel` are the bell's tooltip in each state. Every text field accepts up to 300 characters.

`colors.accent`, `colors.background`, `colors.text`, and `colors.textSecondary` take a hex value or a CSS color name. The Control Panel form validates each field this way, up to 64 characters. The init snippet accepts any non-empty string for these fields without checking its format or length.

<Aside type="note">
A dark-mode palette, the launcher's `zIndex`, and a custom `fontFamily` are available as additional init-param-only fields. They aren't exposed in the Control Panel form described below.
</Aside>

## Managing it in the Control Panel

<Aside type="caution" title="Panel-managed applications only">
The Control Panel card is available only for applications with the Web platform in **panel-managed** mode (see [Web Push SDK 3.0](/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/)). For a snippet-configured application, set `subscriptionWidget` in the init snippet as shown above.
</Aside>

On a panel-managed application, go to **Settings → Configure platforms → Web** and open the **Subscription widget** card. A badge on the card shows whether the widget is **On** or **Off**.

Click **Configure** to edit the same fields described above, grouped as **Prompt**, **When to show**, **Bell**, **Texts**, and **Colors**, next to a live preview that shows both the undecided-visitor and the blocked-visitor state. Saving replaces the panel's copy of the widget config. It does not touch the WebSDK init snippet on your site.

While an application is panel-managed, values saved in this card override the same keys in the site's init snippet. Moving a site to panel configuration is a switch in the panel, with no code change on the site.

## 3. Control the widget from your code

Once the widget bundle has loaded, it publishes an API at `Pushwoosh.moduleRegistry.subscriptionWidget`:

```javascript
Pushwoosh.moduleRegistry.subscriptionWidget.show();          // Opens the prompt
Pushwoosh.moduleRegistry.subscriptionWidget.hide();          // Closes the prompt
Pushwoosh.moduleRegistry.subscriptionWidget.toggle();        // Toggles the current state
Pushwoosh.moduleRegistry.subscriptionWidget.toggle(true);    // Opens the prompt
Pushwoosh.moduleRegistry.subscriptionWidget.isVisible();     // Returns whether the prompt is open
```

`show()` has no effect for a visitor who already subscribed. Opening the prompt dispatches a `show-subscription-widget` event. Closing it dispatches `hide-subscription-widget`:

```javascript
Pushwoosh.addEventHandler('show-subscription-widget', function() {
  console.log('Triggered event: show-subscription-widget');
});

Pushwoosh.addEventHandler('hide-subscription-widget', function() {
  console.log('Triggered event: hide-subscription-widget');
});
```

## Migrating from the older widgets

The push subscription button, the Custom Subscription Popup, and the Subscription Prompt keep working as documented on their own pages as long as `subscriptionWidget.enable` stays unset or `false`. Setting it to `true` stops all three from loading, on both a snippet-configured and a panel-managed application.

<Aside type="caution">
Switching a panel-managed application to the subscription widget does not carry over the settings of whichever old widget it replaces — the widget starts from its own defaults, and you configure it from scratch in its card.
</Aside>