---
title: "External Providers"
description: "Use third-party WebSocket providers for real-time update notifications on any platform."
canonical_url: "https://nuxtseo.com/docs/skew-protection/providers/external"
last_updated: "2026-09-30T11:09:50.250Z"
---

Adapters connect your app to external [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) services for real-time update notifications. Set an adapter through [`updateStrategy`](/docs/skew-protection/api/config#updatestrategy-polling--sse--ws), including on static/prerendered sites.

## Why Use External Providers?

Use an external provider when your host cannot keep connections open or you want the provider to manage them.
Your app subscribes through the provider, so static sites can receive updates too.

## Available Adapters

| Adapter           | Service                                           | Free Tier         |
| ----------------- | ------------------------------------------------- | ----------------- |
| [Pusher](#pusher) | [pusher.com](https://pusher.com){target="_blank"} | 200k messages/day |
| [Ably](#ably)     | [ably.com](https://ably.com){target="_blank"}     | 6M messages/month |

## Pusher

[Pusher Channels](https://pusher.com/channels) is a hosted WebSocket service.

```bash
npx nypm add pusher-js
```

**Configuration:**

```ts [nuxt.config.ts]
import { pusherAdapter } from 'nuxt-skew-protection/adapters/pusher'

export default defineNuxtConfig({
  skewProtection: {
    updateStrategy: pusherAdapter({
      // Required
      key: process.env.PUSHER_KEY,
      cluster: process.env.PUSHER_CLUSTER,
      appId: process.env.PUSHER_APP_ID,
      secret: process.env.PUSHER_SECRET,
      // Optional
      channel: 'skew-protection', // default
      event: 'VersionUpdated', // default
    })
  }
})
```

## Ably

[Ably](https://ably.com) is a realtime messaging platform.

```bash
npx nypm add ably
```

**Configuration:**

```ts [nuxt.config.ts]
import { ablyAdapter } from 'nuxt-skew-protection/adapters/ably'

export default defineNuxtConfig({
  skewProtection: {
    updateStrategy: ablyAdapter({
      // Required
      key: process.env.ABLY_KEY,
      authUrl: '/api/ably-token',
      // Optional
      clientId: 'browser',
      channel: 'skew-protection', // default
      event: 'VersionUpdated', // default
    })
  }
})
```

## Custom Adapters

An adapter has two halves. The client half is a module that the module bundles into the browser build. The build half lives in `nuxt.config` and broadcasts each new build id after a production build. Pusher and Ably use the same mechanism.

Write the client half first. It must export `subscribe`:

```ts [adapter/web.ts]
import { defineWebSubscribe } from 'nuxt-skew-protection/adapters'

export const subscribe = defineWebSubscribe<{ url: string }>((config, onMessage) => {
  const socket = new WebSocket(config.url)
  socket.onmessage = event => onMessage({ version: JSON.parse(event.data).version })
  return () => socket.close()
})
```

Then define the adapter. `web` is an absolute path, an alias such as `~/adapter/web`, or a package specifier:

```ts [adapter/index.ts]
import { fileURLToPath } from 'node:url'
import { defineAdapter } from 'nuxt-skew-protection/adapters'
import { z } from 'zod'

export const myAdapter = defineAdapter({
  name: 'my-adapter',
  schema: z.object({ url: z.string(), token: z.string() }),
  // Only this object reaches the browser. Keep secrets out of it.
  toPublicConfig: ({ url }) => ({ url }),
  web: fileURLToPath(new URL('./web', import.meta.url)),
  // Packages that `web` imports. The build fails if one is missing.
  dependencies: [],
  broadcast: async ({ url, token }, version) => {
    await fetch(url.replace('wss:', 'https:'), {
      method: 'POST',
      headers: { authorization: `Bearer ${token}` },
      body: JSON.stringify({ version }),
    })
  },
})
```

```ts [nuxt.config.ts]
import { myAdapter } from './adapter'

export default defineNuxtConfig({
  modules: ['nuxt-skew-protection'],
  skewProtection: {
    updateStrategy: myAdapter({ url: 'wss://example.com/skew', token: process.env.SKEW_TOKEN! }),
  },
})
```

The build validates the config with `schema`, then calls `broadcast` once the production build finishes. A failed broadcast logs an error and does not fail the build.

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
