Controlled vs uncontrolled
Select works in both controlled and uncontrolled modes:
typescript
1// Uncontrolled — Select manages its own state2<Select label="Color" defaultValue="blue">3 <Select.Option value="red">Red</Select.Option>4 <Select.Option value="blue">Blue</Select.Option>5</Select>67// Controlled — you manage state externally8const [value, setValue] = useState('blue')910<Select label="Color" value={value} onChange={setValue}>11 <Select.Option value="red">Red</Select.Option>12 <Select.Option value="blue">Blue</Select.Option>13</Select>
Uncontrolled for simple cases, controlled for complex state management
Overview
React components can be controlled (parent owns the state) or uncontrolled (component owns its own state). Flint UI supports both patterns for every interactive component.
Uncontrolled (Default)
The simplest approach. The component manages its own state internally. You read the value when you need it (e.g., on form submit).
typescript
1import { Select } from '@flint-ui/core'23function UncontrolledExample() {4 return (5 <form onSubmit={(e) => {6 const data = new FormData(e.currentTarget)7 console.log(data.get('color'))8 }}>9 <Select name="color" defaultValue="blue">10 <Select.Option value="red">Red</Select.Option>11 <Select.Option value="blue">Blue</Select.Option>12 <Select.Option value="green">Green</Select.Option>13 </Select>14 <button type="submit">Submit</button>15 </form>16 )17}
Controlled
The parent component owns the state. Use this when you need to react to changes immediately, validate on change, or sync with other UI.
typescript
1import { useState } from 'react'2import { Select } from '@flint-ui/core'34function ControlledExample() {5 const [color, setColor] = useState('blue')67 return (8 <>9 <Select value={color} onChange={setColor}>10 <Select.Option value="red">Red</Select.Option>11 <Select.Option value="blue">Blue</Select.Option>12 <Select.Option value="green">Green</Select.Option>13 </Select>14 <p>Selected: {color}</p>15 </>16 )17}
When to Use Which
| Pattern | Use When |
|---|---|
| Uncontrolled | Simple forms, no real-time validation needed |
| Controlled | Dependent fields, live preview, complex validation |
Flint UI Convention
Every interactive Flint UI component accepts both patterns:
defaultValue/defaultOpen/defaultChecked→ uncontrolledvalue/open/checked+onChange/onOpenChange→ controlled
If you provide both value and defaultValue, the controlled prop (value) takes precedence.