# Merge refs

One callback ref that hands the element to every ref it is given: an app's ref beside an item's own. Each may be an object (`useRef`), a callback (with or without React 19's cleanup) or `undefined`. When the element goes, each object is emptied, each callback's cleanup runs, and a callback without one is called with `null`.

A new callback each call, so React gives the element again on every render it is called in: in a component, use `useMergedRefs`, which keeps it while the refs stay the same.

```
const own = React.useRef<HTMLDivElement>(null)
return <div ref={mergeRefs(own, props.ref)} />
```

- Registry name: `merge-refs` (`@candor/merge-refs`)
- Tier: Pro. Installing it needs a Pro, Premium or Team licence key: the person runs `npx candor-ui key <key>` themselves; never ask for the key in chat or write it to a file.
- Docs: https://candor.themethodic.com/docs/merge-refs

## Install

```bash
npx candor-ui add merge-refs
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/merge-refs`.
It brings in: tokens.

## Functions

### mergeRefs

```ts
mergeRefs(...refs: PossibleRef<T>[])
```

### useMergedRefs

```ts
useMergedRefs(...refs: PossibleRef<T>[])
```

`mergeRefs` for a component: one callback ref for an app's ref and an item's own, the same function for the component's whole life, so React gives the element once. When a ref it is given changes from one render to the next (an inline callback is new each render), that ref alone is released and the new one given the element, after the render; the others aren't touched.

```
function Panel({ ref, ...props }: React.ComponentProps<"div">) {
  const own = React.useRef<HTMLDivElement>(null)
  return <div ref={useMergedRefs(own, ref)} {...props} />
}
```

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider`: its fixed strings in `strings`, its nouns in `nouns`; entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
