# Slot geometry

> **Info:** This is release candidate documentation for **Stream Chat React SDK v15**. For the latest stable version, see the [latest version (v14)](https://getstream.io/chat/docs/sdk/react/).

Slot geometry reports which slots are **obscured** on screen: collapsed to nothing, or covered by another slot. It only reports: it never moves, hides or closes anything. [Responsive layouts](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/responsive-layouts/) explains when an app needs it and what to do with the result.

Everything on this page imports from `stream-chat-react/slot-geometry`. It's opt-in, and it doesn't depend on `ChatView`: slots are plain strings, so it works with any layout that has named panels.

## The problem it solves

`ChatView` knows which slots are open, not which ones the user can see. On a phone, the app's CSS may lay an open thread over the channel at full width: both slots are open, but the channel is covered. Slot geometry measures the elements that show the slots and reports that, without knowing the app's breakpoints or class names.

## Usage

1. Wrap the layout in `SlotGeometryProvider`.
2. Register the element of each slot with the ref that `useRegisterSlotGeometry(slot)` returns.
3. Read the result with `useSlotGeometry()`.

```tsx
import type { PropsWithChildren, ReactNode } from "react";
import {
  SlotGeometryProvider,
  useRegisterSlotGeometry,
  useSlotGeometry,
} from "stream-chat-react/slot-geometry";

const Panel = ({ children, slot }: PropsWithChildren<{ slot: string }>) => {
  const register = useRegisterSlotGeometry(slot);
  return (
    <div className={`panel panel--${slot}`} ref={register}>
      {children}
    </div>
  );
};

const CoveredNotice = () => {
  const { coverage } = useSlotGeometry();
  return coverage.obscured.channel ? <p>The channel is covered.</p> : null;
};

export const Workspace = ({
  channel,
  thread,
}: {
  channel: ReactNode;
  thread: ReactNode;
}) => (
  <SlotGeometryProvider>
    <Panel slot="channel">{channel}</Panel>
    <Panel slot="thread">{thread}</Panel>
    <CoveredNotice />
  </SlotGeometryProvider>
);
```

`coverage.obscured[slot]` is kept up to date and re-renders the component when it changes: use it to render something. `isObscured(slot)` measures the elements when you call it: use it in an event handler, where the latest change of layout may not have reached `coverage` yet.

[Detecting covered and collapsed slots](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/responsive-layouts/#detecting-covered-and-collapsed-slots) has the CSS that lays a thread over the channel on a phone.

## What counts as obscured

A registered slot is obscured when either holds:

- **It's collapsed:** its area is under 1px². That covers zero width or height, and `display: none`.
- **It's covered:** another registered slot covers at least 60% of its area. A slot beside it, touching only its edge, doesn't count.

The boxes are compared without regard to which one is drawn on top, so two slots laid over each other usually both count as obscured.

The slots are measured again when one of their elements changes size, when the window is resized, and when an element is registered or removed, at most once per animation frame. `coverage` changes only when a slot's result flips. A slot that isn't registered isn't in `coverage.obscured`, and `isObscured` returns `false` for it. Outside a `SlotGeometryProvider`, `useSlotGeometry()` reports nothing as obscured, and its functions do nothing.

## Deferred reveal

Right after a view switch, the slot to reveal may not be measured yet. `requestReveal(slot)` records the intent to reveal it, and `resolveRevealAction(revealSlot, coverage)` says what to do once it's measured:

| Result    | When                                                                    | What to do                             |
| --------- | ----------------------------------------------------------------------- | -------------------------------------- |
| `"wait"`  | `revealSlot` isn't in `coverage.obscured` yet: it hasn't been measured. | Nothing. Wait for the next `coverage`. |
| `"act"`   | The slot is measured and obscured.                                      | Reveal it, then call `clearReveal()`.  |
| `"clear"` | There's no intent, or the slot is measured and visible.                 | Call `clearReveal()`.                  |

`resolveRevealAction` is pure and synchronous, so call it from an effect that depends on `revealSlot` and `coverage`. The intent belongs to the provider, so a provider around `ChatView` keeps it across a view switch. [Deciding what to show](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/responsive-layouts/#deciding-what-to-show) has a complete example: **View** in a thread closes the thread when it covers the channel.

## API

| Export                          | Type                                                                        | Description                                                                                    |
| ------------------------------- | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `SlotGeometryProvider`          | component, takes `children`                                                 | Measures the slots registered inside it.                                                       |
| `useSlotGeometry()`             | `() => SlotGeometryContextValue`                                            | Reads the nearest provider.                                                                    |
| `useRegisterSlotGeometry(slot)` | `(slot: SlotId) => (element: HTMLElement \| null) => void`                  | Returns a stable callback ref that registers the element of `slot`, and removes it on unmount. |
| `resolveRevealAction`           | `(revealSlot: SlotId \| undefined, coverage: SlotCoverage) => RevealAction` | Decides what to do about a pending reveal. See [Deferred reveal](#deferred-reveal).            |

`SlotGeometryContextValue`, what `useSlotGeometry()` returns:

| Member          | Type                                                   | Description                                                                             |
| --------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------- |
| `coverage`      | `SlotCoverage`                                         | `{ obscured: Record<SlotId, boolean> }`, for every registered slot. Kept up to date.    |
| `isObscured`    | `(slot: SlotId) => boolean`                            | Measures the slot now.                                                                  |
| `register`      | `(slot: SlotId, element: HTMLElement \| null) => void` | Registers the element of a slot; `null` removes it. `useRegisterSlotGeometry` calls it. |
| `requestReveal` | `(slot: SlotId) => void`                               | Records the intent to reveal a slot.                                                    |
| `revealSlot`    | `SlotId \| undefined`                                  | The slot an intent names, or `undefined`. Re-renders when it changes.                   |
| `clearReveal`   | `() => void`                                           | Drops the intent.                                                                       |

The types: `SlotId` is `string`. `RevealAction` is `"wait" | "act" | "clear"`. `SlotCoverage` is `{ obscured: Record<SlotId, boolean> }`.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/component-reference/layout/slot-geometry/](https://getstream.io/chat/docs/sdk/react/v15/component-reference/layout/slot-geometry/).