{
  "title": "TextInput",
  "description": "Single-line text input with label, helper text, and validation states.",
  "url": "/docs/components/text-input",
  "since": "0.1.0",
  "tags": [
    "input",
    "form"
  ],
  "platform": "both",
  "source": "---\ntitle: TextInput\ndescription: Single-line text input with label, helper text, and validation states.\nsince: 0.1.0\ntags: [input, form]\nplatform: both\ncategory: inputs\n---\n\nimport { BundleSize } from '@/components/bundle-size';\nimport { ExpoSnack } from '@/components/expo-snack';\nimport { Preview } from '@/components/preview';\nimport { PropsTable } from '@/components/props-table';\n\n<BundleSize component=\"TextInput\" />\n\n## At a glance\n\n- Single-line text field that forwards every native TextInput prop (`value`, `onChangeText`, `placeholder`, `keyboardType`, `secureTextEntry`, …) untouched.\n- Pair with [`Field`](/docs/components/field) for label, description, error wiring, and accessible `id` / `aria-*` attributes.\n- Compose with [`InputGroup`](/docs/components/input-group) when you need fixed prefix / suffix decorators (@username, https://, USD).\n\n## Preview\n\n<Preview name=\"text-input-basic\" />\n\n## Use with Field\n\nFor labelled inputs with description, error, and a11y wiring, wrap in `<Field>`:\n\n```tsx\nimport { Field, TextInput } from '@nori-ui/core';\n\nexport const Example = () => (\n    <Field label=\"Email\" description=\"We won't share it.\">\n        <TextInput placeholder=\"you@example.com\" />\n    </Field>\n);\n```\n\nWith validation error:\n\n```tsx\nimport { Field, TextInput } from '@nori-ui/core';\n\nexport const Example = () => (\n    <Field label=\"Email\" error=\"That doesn't look like a valid email address.\">\n        <TextInput placeholder=\"you@example.com\" />\n    </Field>\n);\n```\n\nSee [Field](/docs/components/field) for the full API. For custom layouts (e.g., a control + button row), use the [compound API](/docs/components/field#custom-layout-compound-api).\n\n## `disabled`\n\nGreys the field, blocks input, and forwards `aria-disabled`. The label\nkeeps its color so the disabled control still reads as a labeled\nfield.\n\n```tsx\nimport { Field, TextInput } from '@nori-ui/core';\n\nexport const Example = () => (\n    <Field label=\"Email\" disabled>\n        <TextInput defaultValue=\"locked@example.com\" />\n    </Field>\n);\n```\n\n## `leading` and `trailing`\n\nInline visual decorations *inside* the field — typically icons or short\nlabels. Unlike [`InputGroup`](/docs/components/input-group) addons,\nthese don't get their own border; they sit flush in the same rounded\nbox as the input.\n\n```tsx\nimport { Search, X } from 'lucide-react-native';\nimport { TextInput } from '@nori-ui/core';\n\nexport const Example = () => (\n    <>\n        <TextInput leading={<Search size={16} />} placeholder=\"Search…\" />\n        <TextInput trailing={<X size={16} />} value={value} onChangeText={setValue} />\n    </>\n);\n```\n\nFor interactive trailing content (a clear button, a visibility\ntoggle), wrap it in a `Pressable` — the slot accepts any React node.\n\n## `containerClassName`\n\nTailwind classes for the *outer wrapper* when used without `<Field>`, targeting the\nbordered field row. Use `className` on `<Field>` for grid sizing when wrapped.\n\n```tsx\nimport { TextInput } from '@nori-ui/core';\n\nexport const Example = () => (\n    <TextInput containerClassName=\"w-full md:w-80\" placeholder=\"Subject\" />\n);\n```\n\n## Native preview\n\n<ExpoSnack component=\"TextInput\" height={350} />\n\n## Props\n\n<PropsTable component=\"TextInput\" />\n"
}
