# Density helpers

Read density tokens (compact, standard, spacious) into JavaScript at runtime.


Two functions read density values into JavaScript. They live in different packages because they target different consumers.

## `useResolvedHeights` — React hook

Lives in `@pretable/react`. Reads `--pretable-row-height` and `--pretable-header-height` from `<html>`'s computed style. Subscribes to attribute changes via `MutationObserver` so the values stay reactive when consumers flip `data-density` or `data-theme`.

```ts
import { useResolvedHeights } from "@pretable/react";

const { rowHeight, headerHeight } = useResolvedHeights();
```

Both return values are numbers (not strings). The hook re-renders the component when either CSS variable changes.

### Optional numeric overrides

If you want JS-side control (e.g., reading from a settings store), pass numbers as arguments:

```ts
const { rowHeight, headerHeight } = useResolvedHeights(
  rowHeightOverride,
  headerHeightOverride,
);
```

Numeric arguments win over CSS-resolved values. Pass `undefined` to defer to CSS for that value.

### Fallback values

If neither argument nor CSS variable resolves to a `<number>px` value, the hook returns built-in fallbacks:

| Variable                   | Fallback |
| -------------------------- | -------- |
| `--pretable-row-height`    | `32`     |
| `--pretable-header-height` | `52`     |

The header-height fallback matches the legacy `HEADER_HEIGHT` constant the engine used before the theming bridge landed, so unmigrated apps see no behavior change. The row-height fallback is conservative (between Excel's 20px compact and Material's 48px standard).

### SSR safety

The hook is SSR-safe. On the server (where `document` is undefined), the snapshot returns the fallback values without DOM access. After hydration on the client, the `MutationObserver` subscribes and the hook returns CSS-resolved values.

### Used internally

`<Pretable>` and `<PretableSurface>` (private) both use `useResolvedHeights` to compute the body viewport height (`viewportHeight - headerHeight`) and to size the sticky header. When you render with `usePretable`, you typically call `useResolvedHeights()` yourself to compute the same — see the example in [Custom rendering](/docs/grid/custom-rendering).

## `getDensityHeights` — vanilla JS snapshot

Lives in `@pretable/ui`. A non-React snapshot — same read logic but without `useSyncExternalStore` or `MutationObserver`.

```ts
import { getDensityHeights } from "@pretable/ui";

const { rowHeight, headerHeight } = getDensityHeights();
```

Returns the same `{rowHeight, headerHeight}` shape as `useResolvedHeights` but does NOT subscribe to changes. Call it once to read current values.

Use this when:

- You're not in a React component (e.g., a vanilla TypeScript utility, a non-React framework, a Node script that needs the values from a DOM snapshot)
- You only need a one-shot read at a specific moment (e.g., page load)
- You want to avoid the React hook's subscription cost (microscopic, but real)

## Which to use

| Situation                                                     | Use                                               |
| ------------------------------------------------------------- | ------------------------------------------------- |
| React component that should re-render on density/theme change | `useResolvedHeights` from `@pretable/react`       |
| Vanilla JS / non-React utility                                | `getDensityHeights` from `@pretable/ui`           |
| One-shot read at component mount (no reactivity needed)       | Either; `getDensityHeights` is slightly leaner    |
| Custom rendering with `usePretable`                           | `useResolvedHeights` (matches the engine's reads) |

## Where to go next

- [Theming > Density switching](/docs/theming/density) — recipe for wiring `data-density` from React state.
- [Custom rendering](/docs/grid/custom-rendering) — using `useResolvedHeights` with `usePretable`.
- [API reference](/docs/grid/api-reference) — full type signatures.
