{"slug":"programmatic-control","title":"Programmatic Control","description":"Controlling the state machines programmatically","contentType":"guides","content":"In some cases, you may want to control the state or context values of a machine\nprogrammatically via its `props` or based on certain conditions. This is\ntypically known as \"controlling\" the components.\n\nZag provides a number of ways to control the state of a machine\nprogrammatically.\n\n## Setting initial context\n\nAll machines support setting the controlled and uncontrolled values for context\nproperties. For example:\n\n- `defaultOpen` and `open` for controlling the open state of disclosure\n  components\n- `defaultValue` and `value` for controlling the value of input machines\n\nFor example, if you want an accordion to start with a specific selected value.\nHere's how to achieve that:\n\n```tsx\nconst service = useMachine(accordion.machine, {\n  defaultValue: [\"item-1\"],\n})\n```\n\n## Controlled Usage\n\nYou can pass the context value to the `useMachine` hook directly and provide the\n`onValueChange` callback to react to the changes.\n\n```jsx\nconst service = useMachine(accordion.machine, {\n  value: props.value,\n  onValueChange(details) {\n    console.log(details)\n  },\n})\n```\n\n## Reactive Context\n\nDifferent frameworks handle reactivity differently when working with Zag\nmachines. Here's how to ensure your machine context stays reactive to prop\nchanges.\n\n### React\n\nReact props are natively reactive with Zag machines. Simply pass them directly\nto `useMachine`\n\n```tsx\nfunction Checkbox(props) {\n  const service = useMachine(checkbox.machine, {\n    checked: props.checked,\n    onCheckedChange: props.onCheckedChange,\n  })\n\n  const api = checkbox.connect(service, normalizeProps)\n  return <label {...api.getRootProps()}>Toggle</label>\n}\n```\n\n### Vue\n\nUse a computed ref or a function that returns the context to make it reactive\n\n```html\n<script setup lang=\"ts\">\n  import { computed } from \"vue\"\n\n  const service = useMachine(\n    checkbox.machine,\n    computed(() => ({\n      checked: props.checked,\n      onCheckedChange: props.onCheckedChange,\n    })),\n  )\n</script>\n```\n\n### Solid\n\nUse a function that returns the context to make it reactive\n\n```tsx\nfunction Checkbox(props) {\n  const service = useMachine(checkbox.machine, () => ({\n    checked: props.checked,\n    onCheckedChange: props.onCheckedChange,\n  }))\n\n  const api = createMemo(() => checkbox.connect(service, normalizeProps))\n  return <label {...api().getRootProps()}>Toggle</label>\n}\n```\n\n### Svelte\n\nTo make props reactive in Svelte, pass a function that returns the context\n\n```svelte\n<script lang=\"ts\">\n  let checked = $state(machineProps.checked ?? false)\n\n  const service = useMachine(checkbox.machine, () => ({\n    ...machineProps,\n    checked,\n    onCheckedChange(details) {\n      checked = details.checked\n      machineProps.onCheckedChange?.(details)\n    },\n  }))\n\n  const api = $derived(checkbox.connect(service, normalizeProps))\n</script>\n\n<label {...api.getRootProps()}>Toggle</label>\n```\n\n**Why this works:**\n\n1. **Reactive Context**: The function re-evaluates whenever dependencies change,\n   ensuring the machine always has current prop values\n2. **State Preservation**: The machine's internal state remains intact while\n   context gets updated\n3. **Framework Integration**: This pattern aligns with the framework's\n   reactivity model\n\nThis approach ensures single-directional data flow while maintaining reactivity.\n\n## Using API methods\n\nThe `connect` method of the machines provide helpful methods (APIs) to change\nthe machine state or update its context.\n\n> This approach is the recommended approach to programmatically update a\n> machine.\n\nLet's say we'd like to change the expanded accordion item in an accordion group.\nHere's how to do that:\n\n```jsx\nfunction Accordion() {\n  // 1. Bind the machine in your framework\n  const service = useMachine(accordion.machine)\n\n  // 2. Call the connect function\n  const api = accordion.connect(service)\n\n  // 3. Use exposed methods\n  api.setValue(\"item-1\")\n\n  return (...)\n}\n```","editUrl":"https://github.com/chakra-ui/zag/edit/main/website/data/guides/programmatic-control.mdx"}