# In-App Experiences for the Web SDK

Instrument your website or web application for in-app experiences. {{< badge_sdk_min web="2+" >}}
The Airship Web SDK v2 supports [Scenes](https://www.airship.com/docs/reference/glossary/#scene), including [Embedded Content](https://www.airship.com/docs/developer/sdk-integration/web/embedded/). See also [Implementing Web SDK v2
](https://www.airship.com/docs/developer/sdk-integration/web/v2-sdk/).

## Instrumenting your Application

In order for Scenes to function, you must [Create a channel](https://www.airship.com/docs/developer/sdk-integration/web/getting-started/#creating-a-channel). This will count the
current browser toward your [Monthly Unique Visitors](https://www.airship.com/docs/reference/billing/#monthly-unique-visitors).

To get the most out of Scenes, instrument your
website or web application to emit events for triggering and event segmentation. The following sections describe the events and provide code samples.

See also:
* [In-App Experience Triggers](https://www.airship.com/docs/guides/messaging/in-app-experiences/configuration/triggers/)
* [Event segmentation](https://www.airship.com/docs/developer/rest-api/ua/schemas/event-segmentation/) in our API reference

### Custom Events

[Custom Events](https://www.airship.com/docs/reference/glossary/#custom_event)
can be used to track any interaction you wish, for example a purchase or a
product view.

**Sending a Custom Event**


```js
const sdk = await UA
const evt = new sdk.CustomEvent('purchase', 34.5, {productId: 1337})
await evt.track()
```


### Screen View Events

Tracking App Screens can make it easy to trigger a Scene or to ensure a Scene is only displayed when currently on a given screen. You must [define App Screens](https://www.airship.com/docs/guides/messaging/in-app-experiences/configuration/app-screens/) in the Airship dashboard.

In the context of a
website or web application, you can think of a "screen" as a page with a
general purpose. For example, `home`, `product_listing`, and `product_detail` are
good generic screen names, whereas `product_id_18957` is too specific and
unlikely to be generally useful for reporting or targeting.

**Tracking a Screen View**


```js
const sdk = await UA
await sdk.analytics.trackScreen('home')
```


### Feature Flag Interactions

When using [Feature Flags](https://www.airship.com/docs/reference/glossary/#feature_flag), you should track when a user has interacted with the feature the flag
controls. The interaction event is included in Feature Flag reporting and can also be used for triggering:

**Tracking Interaction with a Flagged Feature**


```js
const sdk = await UA
const flag = await sdk.components.featureFlags.get('new_product_pages')

// later, when the user interacts with the new product pages
await sdk.components.featureFlags.trackInteraction(flag)
```


### App Version Updates

If you version your web app and wish to be able to target users who may have
seen an earlier version of the app, when initializing the SDK, use the
`appVersion` property set to your app's version number. This will be
passed in your snippet as well as your push worker:

**Setting your App Version**


```js
<script type="text/javascript">
var options = {
  appKey: '<your_app_key>',
  token: '<your_token>',
  vapidPublicKey: '<your_vapid_public_key>',
  // your app version, as `<major>.<minor>.<patch>`
  appVersion: '1.2.1'
}
!function(n,r,e,t,c){var i,o="Promise"in n,u={then:function(){return u},catch:function(n){
return n(new Error("Airship SDK Error: Unsupported browser")),u}},s=o?new Promise((function(n,r){i=function(e,t){e?r(e):n(t)}})):u
;s._async_setup=function(n){if(o)try{i(null,n(c))}catch(n){i(n)}},n[t]=s;var a=r.createElement("script");a.src=e,a.async=!0,a.id="_uasdk",
a.rel=t,r.head.appendChild(a)}(window,document,'https://aswpsdkus.com/notify/v2/ua-sdk.min.js','UA', options);
</script>
```


## Controlling Scenes

If Scenes are enabled for your project, they will display according to their trigger and conditions settings. You can control their display programmatically or disable them completely, if desired.

### Screen Sizes and Breakpoints

When creating Scene [view styles](https://www.airship.com/docs/guides/features/messaging/scenes/scenes/#view-styles) in your project's default settings, you can [define alternative settings that apply to a Scene based on a device's screen size](https://www.airship.com/docs/guides/features/messaging/scenes/conditional-design-overrides/). These sizes are defined by screen width breakpoints in the SDK:

* Small: Less than 1024px
* Medium: Greater or equal to 1024px and less than 1920px
* Large: 1920px and greater

You can override the values by setting custom breakpoints that better match your web application. Pass
the following options in your SDK initialization snippet:

**Setting Screen Sizes**


```js
components: {
  inAppAutomation: {
    displayBreakpoints: {
      // the size at which a screen will be considered "medium", in pixels; inclusive
      medium: 800,
      // the size at which a screen will be considered "large", in pixels; inclusive
      large: 1024
    }
  }
}
```


Anything smaller than "medium" is considered to be a "small" screen,
so no value needs to be set for that breakpoint.

If you set custom breakpoints, also add them to your project settings for more accurate device previews when creating Scenes. Follow the steps for [Setting Scene defaults](https://www.airship.com/docs/guides/messaging/in-app-experiences/configuration/defaults/#scene-defaults) in *In-App Experience Defaults* and set Breakpoint values in a view style's Background settings.
 

### Disabling

To permanently disable Scenes in the current browser, pass the
following options in your SDK initialization snippet:

**Disabling Scenes**


```js
components: {
  inAppAutomation: {
    enabled: false
  }
}
```


### Pausing and Resuming Display

You can choose to pause and resume Scene display. For example,
you might wish to only disable showing new Scenes when opening a modal on
your site or during a checkout flow.

**Pausing and Resuming Scenes**

```js
const sdk = await UA

// pause display of scenes
await sdk.components.inAppAutomation.setPaused(true)
// get the paused status; resolves to a boolean value
await sdk.components.inAppAutomation.isPaused()
// resume display
await sdk.components.inAppAutomation.setPaused(false)
```


By default, displays are not paused. If you'd like to start in a paused
state so that you can manually unpause at a time of your choosing, pass the
following configuration in your SDK initialization snippet:

**Starting Scenes in Paused State**

```js
components: {
  inAppAutomation: {
    startInPausedState: true
  }
}
```


### Display Interval

The display interval controls the amount of time to wait before the manager can display the next triggered Scene. The default value is set to 0 milliseconds and can be adjusted to any amount of time in milliseconds.

**Setting the Display Interval Programmatically**

```js
await sdk.components.inAppAutomation.setDisplayInterval(30000)  // 30 seconds
```


You can also set the display interval via your SDK initialization snippet:
**Setting the Display Interval via Config**

```js
components: {
  inAppAutomation: {
    displayIntervalMs: 30000  // 30 seconds
  }
}
```



### Delegating Display

You can choose to implement your own display delegate, which can control if a
Scene is allowed to be displayed. It can also be notified when a Scene is
displayed or has finished displaying.

Your display delegate must implement the `[InAppDisplayDelegate](https://www.airship.com/docs/reference/libraries/web-notify-sdk/v2-latest/InAppDisplayDelegate.html)
` interface. All
methods are optional.

> **Note:** When using a display delegate, we recommended that you [start display in a paused state](#pausing-and-resuming-display) and then unpause after setting your delegate.
> Otherwise, it is not guaranteed your delegate will be registered before displays
> occur.


**Controlling Display using a Delegate**

```js
const delegate = {
  isMessageReadyToDisplay: (message) => {
    if (myApp.canDisplayMessage(message)) {
      return true
    }
    return false
  }
}
await sdk.components.inAppAutomation.setDisplayDelegate(delegate)
await sdk.components.inAppAutomation.setPaused(false)
```


If you need to pass additional information to your application, you can use
[Custom Keys](https://www.airship.com/docs/guides/messaging/in-app-experiences/configuration/optional-features/#custom-keys) when composing your Scene. They will be available on the `extras` property
of the `[InAppAutomationDetails](https://www.airship.com/docs/reference/libraries/web-notify-sdk/v2-latest/global.html#InAppAutomationDetails)
` that are passed to your delegate.

### Dark Mode

By default, Scenes follow the browser preferences for dark
mode. This behavior can be disabled by a configuration value. When disabled, all
displayed content will use the colors configured for light mode.

**Disabling Automatic Dark Mode**


```js
components: {
  inAppAutomation: {
    matchBrowserDarkMode: false
  }
}
```


## Custom Views

A *Custom View* is a native view from your mobile or web application embedded into a Scene. Custom Views can display any native content your app exposes, so you can reuse that existing content within any screen in a Scene. For a web page, this means embedding
an HTML view into a Scene.

> **Note:** When using Custom Views, you should initialize In-App Experiences in a paused
> state. If you do not, there's no guarantee that your Custom View delegate will
> be registered by the time a Scene displays, and the Custom View would not be
> shown. See the section on [Pausing and Resuming
> Display](#pausing-and-resuming-display) for further information.


### Implementation

To display Custom Views, you must implement the `[InAppCustomViewDelegate](https://www.airship.com/docs/reference/libraries/web-notify-sdk/v2-latest/InAppCustomViewDelegate.html)
` interface
and register it using the `[setCustomViewDelegate](https://www.airship.com/docs/reference/libraries/web-notify-sdk/v2-latest/InAppAutomationManager.html#setCustomViewDelegate)
` method of the `[InAppAutomationManager](https://www.airship.com/docs/reference/libraries/web-notify-sdk/v2-latest/InAppAutomationManager.html)
`. Only one delegate may be registered at a
time.

When it's time for a Custom View to be displayed, your delegate's
`onCustomViewShown` method will be called with the following parameters:

* `name` — The name of the Custom View, which is referenced when adding it to a Scene
* `element` — The HTML element into which your Custom View should render
* `properties` — Any additional properties set for your Custom View as a key/value
  object; all keys and values will be strings

When called, your `onCustomViewShown` method must return an object that
fulfills the `[InAppCustomViewHandler](https://www.airship.com/docs/reference/libraries/web-notify-sdk/v2-latest/InAppCustomViewHandler.html)
` interface. This will contain the `destroy` method, which will be called with no parameters when your view is to be removed.

> **Important:** Your delegate should render synchronously into the provided `element`. If it
> does not, you may cause the Scene's layout to shift after rendering.


### Example Custom View

The following example shows a Custom View that renders an embedded [Google
Map](https://developers.google.com/maps/documentation/embed/embedding-map) when
called to render a Custom View named `map`. This example assumes that you have
started In-App Experiences in a paused state, as recommended above.

In our example, we pass `properties` to the view to determine the location the map
should show. `q` is the query to pass to the map view.

**Example Custom View rendering a map**

```js
// a React Function Component for rendering an embedded Google Map
const Map = ({ q }) => {
  return (
    <iframe
      width="100%"
      height="250"
      frameBorder="0"
      style={{ border: 0 }}
      referrerPolicy="no-referrer-when-downgrade"
      src={`https://www.google.com/maps/embed/v1/place?key=<YOUR_API_KEY>&q=${encodeURIComponent(q)}`}
      allowFullScreen
    />
  )
}

// our delegate method which will be called when the Custom View is displayed
const onCustomViewShown = (name, el, properties) => {
  switch (name) {
    case "map":
      // when the `name` is `map`, render our map view
      const { q } = properties
      ReactDOM.render(<Map q={q} />, el)
      break
  }
  return {
    // remove the react component when the view is destroyed
    destroy: () => ReactDOM.unmountComponentAtNode(el),
  }
}

const sdk = await UA
// define the delegate
const delegate = {onCustomViewShown}
// register the delegate with the Airship SDK
await sdk.components.inAppAutomation.setCustomViewDelegate(delegate)
// resume display
await sdk.components.inAppAutomation.setPaused(false)
```

