npm package: @magicbell/preact
Description: This package contains the Preact components to build a notification UI for your site powered by MagicBell.
Latest version: 1.12.0

This package contains the Preact components to build a notification UI for your site powered by MagicBell.

## Quick Start

```bash
npm install @magicbell/preact
```

## Authentication

You'll need a User JWT to use the SDK. You can generate these on your own server. Read our [Authentication Docs](https://www.magicbell.com/docs/api/authentication/user) to learn how to do this.

## ContextProvider

Provides the MagicBell context to all components in the SDK.

Wrap your app in this component to enable access to the MagicBell API client, local-first user/session state, and shared configuration. Required for components like `Inbox`, `FloatingInbox`, and `SlackButton` to function.

```tsx
import { h, Fragment } from "preact";
import Provider from "@magicbell/preact/context-provider";
import FloatingInbox from "@magicbell/preact/floating-inbox";

function App(props: any) {
  return (
    <Provider token="${USER_JWT}">
      <nav>
        <div>logo</div>
        <FloatingInbox />
      </nav>
      <main>your content here</main>
    </Provider>
  );
}

export default App;
```


**Properties**

| Name | Type | Default | Description |
|------|------|---------|-------------|
| token * | `string` |  | The MagicBell User JWT |
| toasts | `boolean` | `true` | Enable or disable toast notifications. |
| children | `Element` |  | Elements to render inside the provider. |


## FloatingInbox

A floating version of the notification inbox, toggled by a button.

Renders the inbox inside a floating panel positioned relative to a trigger. Ideal for embedding in navigation bars or headers. Supports full customization of placement, dimensions, trigger, and inbox content.

```tsx
import { h, Fragment } from "preact";
import Provider from "@magicbell/preact/context-provider";
import FloatingInbox from "@magicbell/preact/floating-inbox";

function App(props: any) {
  return (
    <Provider token="${USER_JWT}">
      <FloatingInbox
        placement="bottom-start"
        height={500}
        width={400}
        offset={10}
      />
    </Provider>
  );
}

export default App;
```


**Properties**

| Name | Type | Default | Description |
|------|------|---------|-------------|
| defaultOpen | `boolean` | `false` | Whether the inbox should be open by default. |
| height | `number` | `400` | The height of the floating inbox panel in pixels. |
| offset | `OffsetOptions` | `8` | Offset of the inbox from the trigger button, in pixels. Can be a single number or an object with main/cross axis. |
| onNotificationClick | `((event: Event, detail: { notification: Notification; }) => void)` |  | Called when a notification item is clicked. Call `event.preventDefault()` to prevent the default behavior. |
| onOpenChange | `((open: boolean, meta: OpenChangeMeta) => void)` |  | Called whenever the user intends to change the open state. Receives the next state and the reason: 'toggle' \| 'outside' \| 'escape'. |
| open | `boolean` |  | Controlled open state. When provided, component becomes controlled and will not close on outside click or Escape by itself. |
| placement | `Placement` | `bottom-start` | Where to place the inbox relative to the trigger button. E.g. 'bottom-end', 'top-start', etc. |
| width | `number` | `400` | The width of the floating inbox panel in pixels. |
| ButtonComponent | `Component` | `InboxButton` | The component used to trigger the inbox, typically a bell icon. It will receive `onClick` and `ref` props. |
| InboxComponent | `Component` | `Inbox` | The inbox component to render inside the floating panel. Defaults to `<Inbox />` if not provided. |


## Inbox

A notification inbox component that displays a list of notifications for the current user.

Supports pagination, read/unread states, and customizable rendering of notification items. Can be embedded directly in a page or used in combination with floating UIs using FloatingInbox.

```tsx
import "@magicbell/preact/styles/inbox.css";
import { h, Fragment } from "preact";
import Provider from "@magicbell/preact/context-provider";
import Inbox from "@magicbell/preact/inbox";

function App(props: any) {
  return (
    <Provider token="${USER_JWT}">
      <Inbox />
    </Provider>
  );
}

export default App;
```


**Properties**

| Name | Type | Default | Description |
|------|------|---------|-------------|
| height | `number` | `400` | Set the height of the inbox. |
| itemHeight | `number` | `50` | Estimated item height. It gets measured after mount, but it helps to estimate this to limit layout shift. |
| onNotificationClick | `((event: Event, detail: { notification: Notification; }) => void)` |  | Called when a notification item is clicked. Call `event.preventDefault()` to prevent the default behavior. |
| ItemComponent | `Component` |  | Component to use as Notification Item. The component receives two props; `index: number` and `data: Notification`. |


## InlineNotification

An inline notification component that can be used to show an unread notification.


**Properties**

| Name | Type | Default | Description |
|------|------|---------|-------------|
| animate | `"down" \| "up"` |  | The animation direction when a new notification arises. E.g. 'up' or 'down' |
| topic | `string` |  | The topic whose unread notification is indicated. |
| variant | `"bar" \| "dot"` |  | The component to render for an unread notification - 'bar' renders title, sentAt, and content of the notification 'dot' renders a dot indicating an unread notification |
| Component | `any` |  | A custom component rendered if there is an unread notification. |


## SlackButton

A button to subscribe or unsubscribe a user to your Slack channel.

Registers or removes the user’s Slack token using the provided Slack App ID. Supports custom labels, styling, and redirect flow.

```tsx
import "@magicbell/preact/styles/slack-button.css";
import { h, Fragment } from "preact";
import Provider from "@magicbell/preact/context-provider";
import SlackButton from "@magicbell/preact/slack-button";

function App(props: any) {
  return (
    <Provider token="${USER_JWT}">
      <SlackButton
        redirectUrl="https://example.com"
        appId="A06DAT80SHF"
        renderLabel={({ status, error }) =>
          error ||
          (status === "success"
            ? "disable slack notifications"
            : "enable slack notifications")
        }
      />
    </Provider>
  );
}

export default App;
```


**Properties**

| Name | Type | Default | Description |
|------|------|---------|-------------|
| appId * | `string` |  | The Slack App ID used to initiate the installation flow. |
| className | `string` |  | Additional class names to apply to the button. |
| errorLabel | `string` |  | Static label to show when an error occurred. Optional alternative to renderLabel. |
| idleLabel | `string` |  | Label to show when the button is idle. Optional alternative to renderLabel. |
| loadingLabel | `string` |  | Label to show while the current status is being fetched. Optional alternative to renderLabel. |
| onClick | `(event: Event) => void \| false` |  | Called when the button is clicked. Return `false` to prevent the install flow. |
| onStatusChange | `((status: Status, prevStatus: Status \| null) => void)` |  | Called when the subscription status changes. Useful for syncing state, triggering analytics, or updating UI elsewhere. |
| redirectUrl | `string` | `location.href` | Optional URL to redirect the user to after completing the Slack OAuth flow. Defaults to the current origin. |
| renderLabel | `((props: { status: Status; error: string \| null; }) => any)` | `({ status }) => status === 'success' ? 'discard' : 'enable'` | Custom render function for the button label. Receives the current install status and any error message. |
| successLabel | `string` |  | Label to show when the user is subscribed to this channel. Optional alternative to renderLabel. |
| unstyled | `boolean` |  | When true, omit the default base class to fully control styling. |


## SlackbotButton

A button to subscribe or unsubscribe a user to the MagicBell Slackbot channel.


**Properties**

| Name | Type | Default | Description |
|------|------|---------|-------------|
| className | `string` |  | Additional class names to apply to the button. |
| errorLabel | `string` |  | Static label to show when an error occurred. Optional alternative to renderLabel. |
| idleLabel | `string` |  | Label to show when the button is idle. Optional alternative to renderLabel. |
| includeIncomingWebhook | `boolean` |  | When true, request 'incoming-webhook' scope during Slackbot installation. Installing user will be prompted to choose a channel on install. |
| loadingLabel | `string` |  | Label to show while the current status is being fetched. Optional alternative to renderLabel. |
| onClick | `(event: Event) => void \| false` |  | Called when the button is clicked. Return `false` to prevent the install flow. |
| onStatusChange | `((status: Status, prevStatus: Status \| null) => void)` |  | Called when the subscription status changes. Useful for syncing state, triggering analytics, or updating UI elsewhere. |
| redirectUrl | `string` | `location.href` | Optional URL to redirect the user to after completing the Slackbot OAuth flow. Defaults to the current origin. |
| renderLabel | `((props: { status: Status; error: string \| null; }) => any)` | `({ status }) => status === 'success' ? 'discard' : 'enable'` | Custom render function for the button label. Receives the current install status and any error message. |
| successLabel | `string` |  | Label to show when the user is subscribed to this channel. Optional alternative to renderLabel. |
| unstyled | `boolean` |  | When true, omit the default base class to fully control styling. |


## ToastContainer

**Properties**

| Name | Type | Default | Description |
|------|------|---------|-------------|
| className | `string` |  |  |
| duration | `number` |  |  |
| visibleToasts | `number` |  |  |


## UserPreferences

A preferences management component that lets users toggle notification channels per category.


**Properties**

| Name | Type | Default | Description |
|------|------|---------|-------------|
| className | `string` |  | Additional class names to apply to the root container element. |


## WebPushButton

A button to subscribe or unsubscribe a user to Web Push Notifications.

Uses the Push API to register or remove the user’s subscription. The user stays on the page; no redirect is involved. Requires a configured service worker. Supports custom labels, styling, and ref forwarding.

To enable web push, your service worker must import MagicBell’s script:

```js
importScripts('https://assets.magicbell.io/web-push-notifications/sw.js');
```

```tsx
import "@magicbell/preact/styles/webpush-button.css";
import { h, Fragment } from "preact";
import Provider from "@magicbell/preact/context-provider";
import WebPushButton from "@magicbell/preact/webpush-button";

function App(props: any) {
  return (
    <Provider token="${USER_JWT}">
      <WebPushButton
        renderLabel={({ status, error }) =>
          error ||
          (status === "success"
            ? "disable webpush notifications"
            : "enable webpush notifications")
        }
      />
    </Provider>
  );
}

export default App;
```


**Properties**

| Name | Type | Default | Description |
|------|------|---------|-------------|
| className | `string` |  | Additional class names to apply to the button. |
| errorLabel | `string` |  | Label to show when an error occurred. Optional alternative to renderLabel. |
| idleLabel | `string` |  | Label to show when the button is idle. Optional alternative to renderLabel. |
| loadingLabel | `string` |  | Label to show while the current status is being fetched. Optional alternative to renderLabel. |
| onClick | `(event: Event) => void \| false` |  | Called when the button is clicked. Return `false` to prevent the subscription flow. |
| onStatusChange | `((status: WebPushStatus, prevStatus: WebPushStatus \| null) => void)` |  | Called when the subscription status changes. Useful for syncing state, triggering analytics, or updating UI elsewhere. |
| renderLabel | `((props: { status: WebPushStatus; error: string \| null; }) => any)` | `({ status }) => status === 'success' ? 'discard' : 'enable'` | Custom render function for the button label. Receives the current install status and any error message. |
| requestInstallLabel | `string` |  | Label to show when the app needs to be installed as a PWA (iOS requirement). Optional alternative to renderLabel. |
| serviceWorkerPath | `string` | `/sw.js` | Path to your custom service worker file. Defaults to `/sw.js`. The service worker must include MagicBell’s web push script via `importScripts(...)`. |
| successLabel | `string` |  | Label to show when the user is subscribed to this channel. Optional alternative to renderLabel. |
| unstyled | `boolean` |  | When true, omit the default base class to fully control styling. |
| unsupportedLabel | `string` |  | Label to show when web push is not supported by the browser. Optional alternative to renderLabel. |


## Theming

The components come with stylesheets that can be directly imported. 
For example, for the FloatingInbox: 

```javascript
import "@magicbell/preact/styles/floating-inbox.css";
```

### Custom Theme

All of our components come with predefined class names. You can target these classes in your own stylesheet to override or extend the default styles.

For example, you could create a custom dark mode:

```css
body {
  --magicbell-font-family: "Inter", "Helvetica Neue", Arial, sans-serif;

  --magicbell-text-default: #f5f5f5;
  --magicbell-text-link: #3b82f6;

  --magicbell-bg-default: #111827;
  --magicbell-bg-active: #212f47;
  --magicbell-bg-hover: #1f2937;

  --magicbell-surface-bg: #1f2937;
  --magicbell-surface-bg-hover: #2d3748;

  --magicbell-border-muted: #374151;

  --magicbell-spacing-md: 0.75rem;
  --magicbell-spacing-lg: 1rem;
}

body .magicbell--button {
  font-size: 14px;
  font-weight: 600;
}

body .magicbell--inbox-footer a {
  color: #fff;
}
```