> ## Documentation Index
> Fetch the complete documentation index at: https://notifizz.com/llms.txt
> Use this file to discover all available pages before exploring further.

# @notifizz/react SDK Reference

> React reference for @notifizz/react — NotifizzInbox component, NotifizzProvider context, useNotifizz hook, custom bell, headless mode.

# `@notifizz/react` SDK reference

`@notifizz/react` ships the React components and hooks for the Notifizz Notification Center widget — a `NotifizzInbox` component for the default bell, a `NotifizzProvider` for app-wide state access, and a `useNotifizz` hook for reading state and driving the widget from any descendant.

## TL;DR

* `<NotifizzInbox options={...} />` — drop-in component that renders the bell + dropdown and authenticates the widget.
* `<NotifizzProvider options={...}>` + `useNotifizz()` — share notification state across the app via context, or build a fully headless UI.
* Three auth modes via `authType`: `'firebase'`, `'backendToken'`, `'none'` (dev-only).
* `renderBell` prop replaces the default bell with your own UI; `useNotifizz` exposes `unreadCount`, `isOpen`, `isReady`, `open`, `close`, `toggle`.

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install @notifizz/react
  ```

  ```bash yarn theme={null}
  yarn add @notifizz/react
  ```

  ```bash pnpm theme={null}
  pnpm add @notifizz/react
  ```
</CodeGroup>

## Components

### `NotifizzInbox`

Default component. Mounts the widget, renders the bell, and listens for `notifizz:ready` / `notifizz:state` window events.

```jsx theme={null}
import NotifizzInbox from "@notifizz/react";

function App() {
  return (
    <NotifizzInbox
      options={{
        apiKey: "YOUR_FRONT_API_KEY",
        authType: "backendToken",
        token: "TOKEN_FROM_BACKEND",
        userId: "user_42",
        userEmail: "alice@example.com",
        position: "top-right",
      }}
      onReady={() => console.log("widget ready")}
      onStateChange={(state) => console.log("unread:", state.unreadCount)}
    />
  );
}
```

#### Props

| Prop            | Type                                      | Required | Default                          | Description                           |
| --------------- | ----------------------------------------- | -------- | -------------------------------- | ------------------------------------- |
| `options`       | `NotifizzInboxOptions`                    | yes      | —                                | Authentication and display options.   |
| `renderBell`    | `(ctx: NotifizzBellContext) => ReactNode` | no       | `<DefaultBell />`                | Custom bell render function.          |
| `onReady`       | `() => void`                              | no       | —                                | Called once when the widget is ready. |
| `onStateChange` | `(state: NotifizzState) => void`          | no       | —                                | Called on every state change.         |
| `serverUrl`     | `string`                                  | no       | `https://widget.notifizz.com`    | Widget server URL.                    |
| `apiUrl`        | `string`                                  | no       | `https://eu.api.notifizz.com/v1` | API base URL.                         |
| `widgetPath`    | `string`                                  | no       | `/v1/widget.js`                  | Widget script path.                   |
| `mountId`       | `string`                                  | no       | `notifizz-notifications`         | DOM id for the widget mount point.    |

#### `NotifizzInboxOptions`

| Option                     | Type                                            | Required                       | Description                                                     |
| -------------------------- | ----------------------------------------------- | ------------------------------ | --------------------------------------------------------------- |
| `apiKey`                   | `string`                                        | yes                            | Your Front API Key.                                             |
| `authType`                 | `'firebase' \| 'backendToken' \| 'none'`        | yes                            | Authentication strategy.                                        |
| `token`                    | `string`                                        | for `firebase`, `backendToken` | Auth token from your backend.                                   |
| `userId`                   | `string`                                        | for `backendToken`, `none`     | The user's unique identifier.                                   |
| `userEmail`                | `string`                                        | for `backendToken`, `none`     | The user's email address.                                       |
| `position`                 | `NotifizzPosition`                              | no                             | `'top-right' \| 'top-left' \| 'bottom-right' \| 'bottom-left'`. |
| `notificationCenterStyles` | `{ marginTop?: string }`                        | no                             | Notification center style overrides.                            |
| `bellStyles`               | `{ marginRight?: string; marginLeft?: string }` | no                             | Bell style overrides.                                           |

#### `authType: "none"` example (dev only)

```jsx theme={null}
<NotifizzInbox
  options={{
    apiKey: "YOUR_FRONT_API_KEY",
    authType: "none",
    userId: "dev_user_42",
    userEmail: "dev@example.com",
  }}
/>
```

<Warning>
  `authType: "none"` ships unauthenticated widget access — anyone with the `apiKey` can read any user's inbox. Use it for local development only. Production must use `firebase` or `backendToken`.
</Warning>

### `NotifizzProvider`

Wraps a subtree to provide notification state via React context. Use it when multiple components need access to notification state, or when you want a fully headless UI.

```jsx theme={null}
import { NotifizzProvider } from "@notifizz/react";

function App() {
  return (
    <NotifizzProvider
      options={{
        apiKey: "YOUR_FRONT_API_KEY",
        authType: "backendToken",
        token: "TOKEN_FROM_BACKEND",
        userId: "user_42",
        userEmail: "alice@example.com",
      }}
    >
      <YourApp />
    </NotifizzProvider>
  );
}
```

| Prop         | Type                   | Required | Description                         |
| ------------ | ---------------------- | -------- | ----------------------------------- |
| `options`    | `NotifizzInboxOptions` | yes      | Authentication and display options. |
| `serverUrl`  | `string`               | no       | Widget server URL.                  |
| `apiUrl`     | `string`               | no       | API base URL.                       |
| `widgetPath` | `string`               | no       | Widget script path.                 |
| `mountId`    | `string`               | no       | DOM id for the widget mount point.  |
| `children`   | `ReactNode`            | yes      | Subtree that gets context access.   |

## Hooks

### `useNotifizz()`

Read notification state and drive the widget from any component inside a `NotifizzProvider`.

```jsx theme={null}
import { useNotifizz } from "@notifizz/react";

function NotificationBadge() {
  const { unreadCount, isOpen, isReady, toggle } = useNotifizz();

  if (!isReady) return null;

  return (
    <button onClick={toggle}>
      Notifications {unreadCount > 0 && `(${unreadCount})`}
    </button>
  );
}
```

#### Return value: `NotifizzBellContext`

| Property      | Type         | Description                                      |
| ------------- | ------------ | ------------------------------------------------ |
| `unreadCount` | `number`     | Current unread notification count.               |
| `isOpen`      | `boolean`    | Whether the notification center is open.         |
| `isReady`     | `boolean`    | Whether the widget has loaded and authenticated. |
| `toggle`      | `() => void` | Toggle the notification center.                  |
| `open`        | `() => void` | Open the notification center.                    |
| `close`       | `() => void` | Close the notification center.                   |

<Warning>
  `useNotifizz()` must be called from inside a `NotifizzProvider`. Calling it outside throws.
</Warning>

## Custom bell

Replace the default bell with your own component using the `renderBell` prop:

```jsx theme={null}
<NotifizzInbox
  options={{ /* ... */ }}
  renderBell={(ctx) => (
    <div className="my-custom-bell" onClick={ctx.toggle}>
      <span className="bell-icon">🔔</span>
      {ctx.unreadCount > 0 && (
        <span className="badge">{ctx.unreadCount}</span>
      )}
    </div>
  )}
/>
```

The `ctx` object is the same `NotifizzBellContext` that `useNotifizz()` returns.

## Headless mode

<Tip>
  Headless mode gives you full control over the notification UI while Notifizz handles real-time data and state. Pair it with your design system for a fully bespoke notification surface.
</Tip>

Use `NotifizzProvider` + `useNotifizz` without rendering `NotifizzInbox`:

```jsx theme={null}
function App() {
  return (
    <NotifizzProvider options={{ /* ... */ }}>
      <CustomNotificationUI />
    </NotifizzProvider>
  );
}

function CustomNotificationUI() {
  const { unreadCount, isOpen, toggle } = useNotifizz();

  return (
    <div>
      <button onClick={toggle}>
        {isOpen ? "Hide" : "Show"} notifications ({unreadCount})
      </button>
    </div>
  );
}
```

## State shape

`NotifizzState` (read via `onStateChange` or `useNotifizz`):

| Property      | Type       | Description                                          |
| ------------- | ---------- | ---------------------------------------------------- |
| `isReady`     | `boolean`  | `true` once the widget has loaded and authenticated. |
| `isOpen`      | `boolean`  | `true` when the dropdown is open.                    |
| `unreadCount` | `number`   | Current unread notification count.                   |
| `lastUpdated` | `number`   | Timestamp (ms) of the last state change.             |
| `hasError`    | `boolean?` | `true` if the widget hit an auth or network error.   |
| `errorCode`   | `string?`  | Error identifier when `hasError` is `true`.          |

## Exported types

```ts theme={null}
import type {
  NotifizzInboxProps,
  NotifizzInboxOptions,
  NotifizzBellContext,
  NotifizzState,
} from "@notifizz/react";
```

## FAQ

<AccordionGroup>
  <Accordion title="`useNotifizz()` throws 'must be used within a NotifizzProvider'.">
    The component calling `useNotifizz()` is rendered outside the `<NotifizzProvider>` tree. Move the provider higher up — typically wrapping your whole app at the root layout. If you only need state in one place, you can use `<NotifizzInbox onStateChange={...}>` directly without a provider.
  </Accordion>

  <Accordion title="State updates don't trigger re-renders.">
    `useNotifizz` returns React state internally — re-renders are automatic. If your component is memoised (e.g. `React.memo`) and re-rendering only on prop changes, ensure you destructure the values you need from `useNotifizz()` directly inside the component body, not via a stale closure.
  </Accordion>

  <Accordion title="Can I use `NotifizzProvider` and `NotifizzInbox` together?">
    Yes — that's the common pattern. The provider exposes state to descendant components, the inbox renders the bell. Mount the inbox inside the provider tree once.
  </Accordion>

  <Accordion title="The widget mounts twice in dev mode (StrictMode).">
    React 18+ `StrictMode` intentionally runs effects twice in dev. The widget guards against duplicate mounts via `cancelled` flags and a `readyFired` ref — you should not see two bells, but you may see two ready callbacks fire briefly. Production builds run effects once.
  </Accordion>

  <Accordion title="'Invalid hook call' / 'Cannot read properties of null (reading useState)'.">
    This is almost always **two copies of React loaded in your app**, not a bug in the SDK. `@notifizz/react` declares `react` and `react-dom` as peer dependencies and uses yours — but a monorepo, a locally-linked package that ships its own React, a mismatched transitive version, or a misconfigured bundler can pull in a second one. React's hook dispatcher then comes back `null` and any hook (including the SDK's) crashes.

    First, confirm there's really a duplicate — this should print a single version each:

    ```bash theme={null}
    npm ls react react-dom      # or: pnpm why react react-dom
    ```

    Then force a single instance in your bundler:

    <CodeGroup>
      ```ts Vite theme={null}
      // vite.config.ts
      import { defineConfig } from 'vite';

      export default defineConfig({
        resolve: { dedupe: ['react', 'react-dom'] },
      });
      ```

      ```js webpack theme={null}
      // webpack.config.js
      const path = require('path');

      module.exports = {
        resolve: {
          alias: {
            react: path.resolve(__dirname, 'node_modules/react'),
            'react-dom': path.resolve(__dirname, 'node_modules/react-dom'),
          },
        },
      };
      ```

      ```js Next.js theme={null}
      // next.config.js — needed only when a linked/local package brings its own React
      module.exports = { transpilePackages: ['@notifizz/react'] };
      ```

      ```ini pnpm theme={null}
      # .npmrc — hoist a single shared copy for peer dependencies
      dedupe-peer-dependents=true
      ```
    </CodeGroup>

    If a locally-linked dependency carries its own `node_modules/react`, deleting that nested copy also resolves it.
  </Accordion>

  <Accordion title="How do I switch user without unmounting the provider?">
    Pass new `options.userId` / `options.token` — the inbox effect is keyed on `options.apiKey` and `options.authType`, so changes to those re-authenticate. For a clean swap (no flash of the previous user's inbox), conditionally render the provider so it unmounts/remounts when the user changes.
  </Accordion>

  <Accordion title="Server-rendering the page — what happens?">
    The widget is browser-only — `NotifizzInbox` and `NotifizzProvider` no-op when `window` is undefined. SSR your page normally; the widget hydrates and authenticates on the client. The rendered HTML contains the mount `<div>` but no widget content until the script loads.
  </Accordion>
</AccordionGroup>

## See also

<CardGroup cols={2}>
  <Card title="Notification Center overview" icon="bell" href="/docs/sdks/notification-center/overview">
    Lifecycle, state model, positioning, custom bell.
  </Card>

  <Card title="Authentication overview" icon="key" href="/docs/sdks/authentication/overview">
    Pick the right widget auth mode.
  </Card>

  <Card title="Frontend quickstart" icon="rocket" href="/docs/quickstart/frontend">
    Get the widget rendering in under five minutes.
  </Card>

  <Card title="Backend quickstart" icon="satellite-dish" href="/docs/quickstart/backend">
    Send the events the widget will display.
  </Card>
</CardGroup>
