import { on, type Handle } from '@remix-run/ui' /** * @name Controlled and Uncontrolled Values * @description Compares controlled props with uncontrolled DOM values across rerenders and remounts. */ export default function App(handle: Handle) { let controlledText = 'hello' let controlledChecked = true let controlledChoice = 'alpha' let uncontrolledTextSnapshot = 'type to update this' let uncontrolledCheckedSnapshot = true let renderCount = 0 let uncontrolledVersion = 0 let rerender = () => { renderCount++ handle.update() } let resetControlled = () => { controlledText = 'hello' controlledChecked = true controlledChoice = 'alpha' rerender() } let remountUncontrolled = () => { uncontrolledVersion++ uncontrolledTextSnapshot = 'type to update this' uncontrolledCheckedSnapshot = true rerender() } return () => (

Controlled vs Uncontrolled Values

Render count: {renderCount}

Controlled

These values come from component state. The text input allows everything except digits, and invalid input does not call update.

State snapshot: text={JSON.stringify(controlledText)}, checked= {String(controlledChecked)}, choice= {JSON.stringify(controlledChoice)}

Uncontrolled

These initialize from defaultValue/defaultChecked once and then keep their own DOM state.

Last DOM snapshot: text={JSON.stringify(uncontrolledTextSnapshot)}, checked= {String(uncontrolledCheckedSnapshot)}
) }