TextInput
A theme-aware text input primitive. Supports controlled and uncontrolled usage, multiline (textarea), error and disabled states, and all standard form attributes.
Architecture: TextInputRoot is a styled <div> that owns the visible border and focus ring. The native <input> (or <textarea>) sits inside it with a transparent background, so the root border appears as the field border. This makes it easy to add prefix/suffix adornments later.
Examples
Default
<TextInput resize={false} placeholder="Enter a value..." />
States
Default
Disabled
Disabled + value
Error
Error + value
<Flex flexDirection="column" gap={2} maxWidth="360px">
{(
[
{ label: 'Default', props: { placeholder: 'Default' } },
{ label: 'Disabled', props: { placeholder: 'Disabled', disabled: true } },
{
label: 'Disabled + value',
props: { value: 'Cannot edit', disabled: true, onChange: () => {} },
},
{ label: 'Error', props: { placeholder: 'Invalid field', error: true } },
{
label: 'Error + value',
props: { value: 'bad@', error: true, onChange: () => {} },
},
] as const
).map(({ label, props }) => (
<Flex key={label} flexDirection="row" alignItems="center" gap={3}>
<Typography variant="caption" color="secondary" width="8rem" flexShrink={0} m={0}>
{label}
</Typography>
<TextInput color="primary" fullWidth {...props} />
</Flex>
))}
</Flex>
Variants
NormalDisabledError
outlined
underline
text
<Flex flexDirection="column" gap={3} maxWidth="560px">
<Flex flexDirection="row" gap={2} mb={1}>
{(['Normal', 'Disabled', 'Error'] as const).map((h) => (
<Typography key={h} variant="caption" color="secondary" flex={1} m={0}>
{h}
</Typography>
))}
</Flex>
{(['outlined', 'underline', 'text'] as const).map((variant) => (
<Flex key={variant} flexDirection="column" gap={1}>
<Typography variant="caption" color="secondary" m={0}>
{variant}
</Typography>
<Flex flexDirection="row" gap={2} alignItems="center">
<TextInput variant={variant} color="primary" placeholder="Normal" fullWidth />
<TextInput
variant={variant}
color="primary"
placeholder="Disabled"
disabled
fullWidth
/>
<TextInput variant={variant} color="primary" placeholder="Error" error fullWidth />
</Flex>
</Flex>
))}
</Flex>
Colors
Default (click)DisabledError
primary
secondary
success
error
info
warning
<Flex flexDirection="column" gap={3} maxWidth="560px">
<Flex flexDirection="row" gap={2} mb={1}>
{(['Default (click)', 'Disabled', 'Error'] as const).map((h) => (
<Typography key={h} variant="caption" color="secondary" flex={1} m={0}>
{h}
</Typography>
))}
</Flex>
{(['primary', 'secondary', 'success', 'error', 'info', 'warning'] as const).map((color) => (
<Flex key={color} flexDirection="column" gap={1}>
<Typography variant="caption" color="secondary" m={0}>
{color}
</Typography>
<Flex flexDirection="row" gap={2}>
<TextInput variant="outlined" color={color} placeholder="Click to focus" fullWidth />
<TextInput variant="outlined" color={color} placeholder="Disabled" disabled fullWidth />
<TextInput variant="outlined" color={color} placeholder="Error" error fullWidth />
</Flex>
</Flex>
))}
</Flex>
Multiline - Auto-grow
Grows with content - no fixed height
<Flex flexDirection="column" gap={1} maxWidth="480px">
<Typography variant="caption" color="secondary" m={0}>
Grows with content - no fixed height
</Typography>
<TextInput
multiline
resize
fullWidth
placeholder="Start typing... the field expands as you write."
/>
</Flex>
Multiline - Fixed rows
Fixed to 4 rows - scrolls when content overflows
<Flex flexDirection="column" gap={1} maxWidth="480px">
<Typography variant="caption" color="secondary" m={0}>
Fixed to 4 rows - scrolls when content overflows
</Typography>
<TextInput rows={4} fullWidth placeholder="Write something..." />
</Flex>
Full Width
const args: Partial<TextInputProps> = {}
<Flex width="100%" maxWidth="480px">
<TextInput {...args} fullWidth placeholder="Full-width input..." />
</Flex>
Types
text
email
password
number
search
url
<Flex flexDirection="column" gap={2} maxWidth="360px">
{(
[
{ type: 'text', placeholder: 'Text' },
{ type: 'email', placeholder: '[email protected]' },
{ type: 'password', placeholder: 'Password' },
{ type: 'number', placeholder: '0' },
{ type: 'search', placeholder: 'Search...' },
{ type: 'url', placeholder: 'https://' },
] as const
).map(({ type, placeholder }) => (
<Flex key={type} flexDirection="row" alignItems="center" gap={3}>
<Typography variant="caption" color="secondary" width="5rem" flexShrink={0} m={0}>
{type}
</Typography>
<TextInput type={type} placeholder={placeholder} fullWidth />
</Flex>
))}
</Flex>