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

# Widget Provider

> Switches the player between an inline and a floating widget layout based on visibility.

<Tabs>
  <Tab title="Code">
    ```jsx WidgetProvider.jsx theme={null}
    "use client";

    import React, { cloneElement, createContext, useContext, useState } from "react";
    import { useInView } from "react-intersection-observer";

    import { usePlayerSelector } from "@/registry/react/PlayerProvider";

    export const WidgetContext = createContext(null);

    export const useWidgetContext = () => {
      const context = useContext(WidgetContext);
      if (!context) {
        throw new Error("useWidgetContext must be used within a WidgetProvider");
      }
      return context;
    };

    export function WidgetConsumer({ children }) {
      const context = useWidgetContext(WidgetContext);
      return children(context);
    }

    export function WidgetProvider({ children }) {
      const playbackState = usePlayerSelector((p) => p.playbackState);
      const [ref, inView] = useInView({ threshold: 0 });
      const [closed, setClosed] = useState(false);
      const mode = inView || closed || playbackState === "stopped" ? "inline" : "widget";
      const close = () => setClosed(true);
      return (
        <WidgetContext.Provider value={{ mode, close }}>
          {cloneElement(React.Children.only(children), { ref })}
        </WidgetContext.Provider>
      );
    }
    ```
  </Tab>
</Tabs>

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npx @beyondwords/cli components add widget-provider
  ```

  ```bash pnpm theme={null}
  pnpm dlx @beyondwords/cli components add widget-provider
  ```

  ```bash yarn theme={null}
  yarn dlx @beyondwords/cli components add widget-provider
  ```

  ```bash bun theme={null}
  bunx --bun @beyondwords/cli components add widget-provider
  ```
</CodeGroup>

## Usage

Wrap a single element in `WidgetProvider` and read the `mode` with
`WidgetConsumer` (a render prop) or the `useWidgetContext` hook.

```tsx theme={null}
import { PlayerProvider } from '@/registry/react/PlayerProvider';
import { WidgetProvider, WidgetConsumer } from '@/registry/react/WidgetProvider';

export function Player() {
  return (
    <PlayerProvider args={{ projectId: 9504, contentId: "ba00ef51-03ac-4a85-8b98-e45c957e8ef8", showUserInterface: false }}>
      <WidgetProvider>
        <WidgetConsumer>
          {({ mode, close }) => (
            <div className={mode === 'widget' ? 'fixed right-4 bottom-4' : ''}>
              {/* your controls */}
              {mode === 'widget' && <button onClick={close}>×</button>}
            </div>
          )}
        </WidgetConsumer>
      </WidgetProvider>
    </PlayerProvider>
  );
}
```

`WidgetProvider` tracks whether its child is on screen (via
`react-intersection-observer`). It exposes:

* **`mode`**: `"inline"` while the child is in view, the player is `stopped`,
  or the widget was closed. Otherwise `"widget"` (e.g. a floating mini-player
  once the user scrolls past it).
* **`close()`**: pins `mode` back to `"inline"` permanently.

`WidgetProvider` clones its single child to attach the visibility `ref`, so it
must wrap exactly one element. Read the value lower in the tree with
`useWidgetContext()`, which throws if used outside a `WidgetProvider`.

## Reference

|                       |                                                                                                                     |
| --------------------- | ------------------------------------------------------------------------------------------------------------------- |
| **Exports**           | `WidgetProvider`, `WidgetConsumer`, `useWidgetContext`                                                              |
| **Provides**          | `mode` (`"inline"` or `"widget"`), `close()`                                                                        |
| **Player state read** | `playbackState`                                                                                                     |
| **Requires**          | a [`PlayerProvider`](/player-ui/react/player-provider) ancestor, and `WidgetProvider` must wrap exactly one element |
