# Pickers

How comments find the element they point at, and how to bring your own picker.

A picker runs in your pages during development and answers three questions for Bud: what is at this point, where is this element now, and which element does the person choose. Bud's screenshot tabs ask it when you comment on the canvas and whenever a page changes; Pages asks it when you pick an element. Choose one with a plugin in your [configuration](/docs/app/getting-started/configuration#picker).

## What a comment remembers

| Part    | Holds                                                                                           |
| ------- | ----------------------------------------------------------------------------------------------- |
| Element | A selector path, its tag, text and a fingerprint of both, and where the point sits inside it.   |
| Block   | The landmark around it that outlives its details: a card, list item, section or component root. |
| Source  | With a framework picker: the component, file, line and column that rendered it.                 |
| Picker  | Which picker described it.                                                                      |

To find an element again, Bud tries its path, then the same tag and text anywhere on the page (or inside its block), then the block itself when its label still matches. Anything else counts as gone, and the pin is marked detached rather than moved onto something else.

## React

`react()` reads the component and its source line from React's development build and your build's source maps, inline or beside each file. It runs in pages without your dev server too, such as a branch's build, where Bud adds the picker itself. Production builds keep no component information, so their comments name only the page's elements.

## React Grab

`reactGrab()` does the same, and picks in Pages with the [React Grab](https://react-grab.com) your project already runs:

- Install `react-grab` in the project and import it in development, as its guide describes. Bud imports the same module, so it starts React Grab if your page hasn't yet.
- The component and line come from React's development build. Production builds have no source to read, and Bud never captures them.
- Picking in Pages uses React Grab's own overlay. Bud takes the element you choose and skips the copy.
- React Grab draws its toolbar over your site, so hide it from screenshots with `previews.css: '[data-react-grab] { display: none !important }'`.

## Your own picker

Add `picker()` with a browser module in your project, ending in `.ts`, `.tsx`, `.js` or `.jsx`. Its default export adds what your framework knows; every method is optional, and Bud's DOM picker covers whatever it leaves out.

```ts title="picker.ts"
import type { PickerAdapter } from '@bud.sh/app'

const picker: PickerAdapter = {
  // What the framework knows about an element.
  describe(element) {
    let block = element.closest('[data-component]')
    let component = block?.getAttribute('data-component')
    if (!component) return null
    let source = { component, file: null, line: null, column: null }
    return { label: component, source }
  },
  // The block that outlives the element's details.
  block(element) {
    return element.closest('[data-component]')
  },
}

export default picker
```

```ts title="bud.config.ts"
import { defineConfig } from '@bud.sh/app'
import { picker } from '@bud.sh/app/plugins'

export default defineConfig({
  routes: ['/'],
  plugins: [picker('./picker.ts')],
})
```

| Method              | Returns                                                                                                          |
| ------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `describe(element)` | `{ label?, source? }`, or `null`. `source.file` is a path as your dev server serves it, such as `/src/card.tsx`. |
| `block(element)`    | The element's block, or `null` for Bud's landmarks.                                                              |
| `pick(signal)`      | The element the person chose with your own interface, or `null`. End it when `signal` aborts.                    |

Your dev server serves the module like any other, so it can import your framework's packages. It runs in your pages only while Bud is open, receives nothing from Bud, and what it returns is checked before Bud stores it. Keep it quick: Bud waits 15 seconds at most before it saves a comment without an element.
