# Web SDK 2.1

> What changes when you move the Grovs Web SDK from 2.0 to 2.1.

Source: https://docs.grovs.io/docs/upgrade-guides/web-2-1

2.1.0 adds [in-app messages](https://docs.grovs.io/docs/sdk/web/in-app-messages) and the [smart banner](https://docs.grovs.io/docs/sdk/web/smart-banners), and removes the messages inbox. Existing code keeps working. Both new features are on by default, so decide on them before you ship.

## Checklist

1. [Install 2.1.0](#1-install-210).
2. [Decide on the smart banner and in-app messages](#2-decide-on-the-smart-banner-and-in-app-messages): both are on by default.
3. [Remove the messages inbox](#3-remove-the-messages-inbox) from your UI.
4. [Handle the new `GrovsError` code](#4-handle-grovserrorinappdisplayfailed) if you switch over it.
5. [Rename custom events that use a reserved name](#5-rename-reserved-event-names).
6. [Check your Content-Security-Policy](#6-check-your-content-security-policy) if you send one.
7. [Check the other changes](#7-other-changes).

## 1. Install 2.1.0

```bash
npm install grovs@2.1.0
```

## 2. Decide on the smart banner and in-app messages

Nothing shows until a banner or a campaign is configured in the dashboard. After that, they show without a code change.

- **Smart banner.** Shown on iPhone, iPad and Android browsers only. To keep it off, pass `smartBanner: false` to `configure()`. Pass it in your own app's webviews too. If you keep it, call `Grovs.smartBanner.setLinkData()` on product pages so **Open in app** lands on the right screen, and offset fixed headers with `--grovs-smart-banner-top`. See [Smart banners](https://docs.grovs.io/docs/sdk/web/smart-banners).
- **In-app messages.** To hold them back on some pages, use `Grovs.inApp.setExcludedScreens()` or `Grovs.inApp.pause()`. See [In-app messages](https://docs.grovs.io/docs/sdk/web/in-app-messages).

The deprecated 1.x constructor (`new Grovs(apiKey, …)`) never shows the smart banner; use `Grovs.configure()` to get it.

## 3. Remove the messages inbox

The inbox UI and automatic message display are gone on the device and the backend. The methods remain as no-ops that make no request:

| Method                             | Now                                        |
| ---------------------------------- | ------------------------------------------ |
| `Grovs.showMessagesList()`         | does nothing                               |
| `Grovs.displayAutomaticMessages()` | does nothing                               |
| `Grovs.getMessages(page)`          | resolves `[]`                              |
| `Grovs.numberOfUnreadMessages()`   | resolves `0`                               |
| `Grovs.markMessageAsRead(id)`      | resolves `true`, also before `configure()` |

The v1 `showMessagesList()` does nothing, and `getMessages()`, `getNumberOfUnreadMessages()` and `markMessageAsRead()` call their success callback with `[]`, `0` and `true`. The `messagesTheme` option is accepted and ignored; the `MessagesTheme` and `GrovsMessage` types remain for existing imports.

Remove inbox buttons and unread badges from your pages.

## 4. Handle `GrovsError.inAppDisplayFailed`

New code `inAppDisplayFailed` (5): an in-app message couldn't be drawn. An exhaustive `switch`, such as one with a TypeScript `never` check, needs a case for it:

```typescript
import Grovs, { GrovsError } from "grovs";

await Grovs.configure({
  apiKey: "your-api-key",
  onError: (code, message) => {
    switch (code) {
      case GrovsError.inAppDisplayFailed:
        console.info("grovs in-app:", message);
        break;
      default:
        reportToSentry(message);
    }
  },
});
```

## 5. Rename reserved event names

`track()` refuses every name the backend rejects. Rename any custom event that uses one:

`in_app_viewed`, `in_app_clicked`, `in_app_dismissed`, `in_app_answered`, `in_app_commented`, `in_app_held`, `push_open`, `push_delivered`, `push_held`, `journey_entered`, `journey_held`, `journey_path`.

## 6. Check your Content-Security-Policy

HTML in-app messages run in a sandboxed iframe that inherits your page's CSP. They need inline scripts allowed in `script-src`, with no nonce, hash or `'strict-dynamic'`. A nonce-, hash- or `'strict-dynamic'`-based policy can't run them; the SDK then skips the message and reports `inAppDisplayFailed` once. The built-in templates work under any policy. See [Content-Security-Policy](https://docs.grovs.io/docs/sdk/web/in-app-messages#content-security-policy).

## 7. Other changes

- **Bundle size.** About 15 KB more compressed, about 40 KB in all, because in-app messages and the smart banner ship in the one file.
- **Same-settings `configure()`.** Calling `configure()` again with the same settings on an authenticated client keeps the client and takes the new callbacks: no re-authentication, no identity re-push, and the smart banner doesn't flicker or count a second view.
- **SPA navigation.** Route tracking moved into a shared observer, so the banner follows route changes even with `autoTrackScreenViews: false`. Screen tracking behaves as before.
- **New events.** `smart_banner_view`, `smart_banner_click` and `smart_banner_dismiss`, plus the in-app events above, appear in your analytics.

## Verify the upgrade

1. Open a Grovs link to your site and check that `onDeeplink` still receives the payload.
2. On a phone browser, open a page a dashboard banner targets and check the banner, then tap **Open in app**.
3. In the dashboard, create an in-app campaign for web, use **Send test** with your own user identifier, then switch away from the tab and back after a minute. The message shows at that sync.

Full list of changes: [Web SDK CHANGELOG](https://github.com/grovs-io/grovs-js/blob/main/CHANGELOG.md) and [MIGRATION.md](https://github.com/grovs-io/grovs-js/blob/main/MIGRATION.md).
