# Controllers

Read and change a component from outside its parts.

A controller connects code to a mounted component. Use one when the button or action that changes it lives somewhere else.

## Connect a Controller

Create it once in your component and pass it to the root:

```tsx
import { createToggleController, Toggle } from "@ui-sdk/solid/toggle"

function Example() {
  const toggle = createToggleController()

  return (
    <>
      <button onClick={() => toggle.toggle()}>Toggle Bold</button>
      <Toggle controller={toggle}>Bold</Toggle>
    </>
  )
}

export { Example }
```

Controller methods run the same change callbacks as user input. Controlled values and cancellation follow [State and Events](/docs/solid/state-and-events).

Each component lists its methods, such as `open()`, `close()`, `setValue()`, or `focus()`.

A read like `toggle.isPressed()` answers when you call it, so call it from an event handler or async code. It returns `undefined` on the server and until the component is created, so markup that reads it renders differently on the server and the client. JSX doesn't update when the state changes either. To render the state, keep it in a signal with `pressed` and `onPressedChange`.

## Share a Popup

External triggers can use the same controller. A `payload` tells the content which item was opened:

```tsx
import {
  createPopoverController,
  Popover,
  PopoverContent,
  PopoverTitle,
  PopoverTrigger,
} from "@ui-sdk/solid/popover"

function Example() {
  const editor = createPopoverController<string>()

  return (
    <>
      <PopoverTrigger controller={editor} payload="Notes">
        Edit Notes
      </PopoverTrigger>
      <PopoverTrigger controller={editor} payload="Tasks">
        Edit Tasks
      </PopoverTrigger>
      <Popover controller={editor}>
        <PopoverContent>
          {(state) => (
            <PopoverTitle>Edit {String(state.payload ?? "")}</PopoverTitle>
          )}
        </PopoverContent>
      </Popover>
    </>
  )
}

export { Example }
```

## Wait for a Popup to Close

A popup controller's `open()` returns a promise. It settles when the popup closes, with its reason and return value:

```ts
const result = await editor.open({ payload: "Notes" })
console.log(result.reason, result.returnValue)
```

For confirmations, [Alert Dialog](/docs/solid/components/alert-dialog) provides `createConfirm()`, which gives you a function you can await.
