# Quickstart — Next.js

Integrate `@ios-web-bluetooth/core` with Next.js (App Router, 13+). Web Bluetooth is a client-side-only API, so every integration path is client-component.

## Prerequisites

- The WebBLE Safari Web Extension enabled on your origin. See the [base quickstart](quickstart.md).
- Next.js 13+ (App Router) or Next.js 12+ (Pages Router).

## 1. Install

```bash
npm install @ios-web-bluetooth/core @ios-web-bluetooth/react
```

## 2. Mount the polyfill in a client-only boundary

Do **not** import `@ios-web-bluetooth/core` in a server component — it touches `window` at import time.

```tsx
// app/providers.tsx
'use client';
import { useEffect } from 'react';

export function BluetoothProvider({ children }: { children: React.ReactNode }) {
  useEffect(() => {
    import('@ios-web-bluetooth/core/auto');
  }, []);
  return <>{children}</>;
}
```

```tsx
// app/layout.tsx
import { BluetoothProvider } from './providers';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html><body><BluetoothProvider>{children}</BluetoothProvider></body></html>
  );
}
```

`dynamic import inside useEffect` ensures the polyfill only runs in the browser.

## 3. Read a heart-rate monitor (client component)

```tsx
// app/heart-rate/page.tsx
'use client';
import { useState } from 'react';

export default function HeartRate() {
  const [bpm, setBpm] = useState<number | null>(null);

  async function connect() {
    const device = await navigator.bluetooth.requestDevice({
      filters: [{ services: ['heart_rate'] }]
    });
    const server = await device.gatt!.connect();
    const service = await server.getPrimaryService('heart_rate');
    const char = await service.getCharacteristic('heart_rate_measurement');
    await char.startNotifications();
    char.addEventListener('characteristicvaluechanged', (ev) => {
      const v = (ev.target as BluetoothRemoteGATTCharacteristic).value!;
      setBpm(v.getUint8(0) & 0x01 ? v.getUint16(1, true) : v.getUint8(1));
    });
  }

  return (
    <>
      <button onClick={connect}>Connect</button>
      <p>{bpm ?? '—'} bpm</p>
    </>
  );
}
```

## 4. Pages Router equivalent

```tsx
// pages/heart-rate.tsx
import dynamic from 'next/dynamic';

const HeartRate = dynamic(() => import('../components/HeartRate'), { ssr: false });
export default function Page() { return <HeartRate />; }
```

`ssr: false` ensures the component — and the polyfill import inside it — only loads in the browser.

## Next

- [API reference](api-reference.md)
- [Recipes](recipes.md)
- [Premium APIs](premium.md)
