static/app/components/core/input/input.mdx
import {Fragment, useState} from 'react';
import {Input} from '@sentry/scraps/input'; import {Stack} from '@sentry/scraps/layout';
import * as Storybook from 'sentry/stories';
export const documentation = import('!!type-loader!@sentry/scraps/input');
<Input> is a fundamental form component for capturing text input from users. It provides a consistent, accessible interface for text entry with support for various sizes, states, and behaviors.
Use <Input> when you need a simple text input field. For inputs with leading or trailing elements (like icons or buttons), use <InputGroup> or <InputControl> instead.
<Input placeholder="Enter your name" defaultValue="" />
The <Input> component comes in three sizes: md (default), sm, and xs. Choose the size that matches your layout and content hierarchy.
export function SizesDemo() { return ( <Storybook.Grid columns={2}> <Stack gap="sm"> <span>size="md" (default)</span> <Input size="md" defaultValue="" /> </Stack> <Stack gap="sm"> <span>size="sm"</span> <Input size="sm" defaultValue="value" /> </Stack> <Stack gap="sm"> <span>size="xs"</span> <Input size="xs" defaultValue="" placeholder="placeholder" /> </Stack> </Storybook.Grid> ); }
<Storybook.Demo> <SizesDemo /> </Storybook.Demo>
<Input size="md" defaultValue="" />
<Input size="sm" defaultValue="value" />
<Input size="xs" defaultValue="" placeholder="placeholder" />
Inputs support three non-editable states: disabled, readOnly, and aria-disabled. These states prevent user input but communicate different meanings:
export function StatesDemo() { const [disabledValue, setDisabledValue] = useState('this is disabled'); const [ariaDisabledValue, setAriaDisabledValue] = useState('this is aria-disabled'); const [readonlyValue, setReadonlyValue] = useState('this is readonly'); return ( <Storybook.Grid columns={2}> <Stack gap="sm"> <span>disabled</span> <Input disabled value={disabledValue} onChange={e => setDisabledValue(e.target.value)} /> </Stack> <Stack gap="sm"> <span>aria-disabled</span> <Input aria-disabled value={ariaDisabledValue} onChange={e => setAriaDisabledValue(e.target.value)} /> </Stack> <Stack gap="sm"> <span>readOnly</span> <Input readOnly value={readonlyValue} onChange={e => setReadonlyValue(e.target.value)} /> </Stack> </Storybook.Grid> ); }
<Storybook.Demo> <StatesDemo /> </Storybook.Demo>
// Disabled: non-interactive, not submitted
<Input disabled value={value} onChange={setValue} />
// Aria-disabled: styled as disabled but remains interactive
<Input aria-disabled value={value} onChange={setValue} />
// Read-only: non-editable but focusable and submitted
<Input readOnly value={value} onChange={setValue} />
[!NOTE] Use
readOnlywhen you want to display a value that should be submitted with the form but not edited. Usedisabledwhen the input is temporarily unavailable. Usearia-disabledwhen you need the disabled appearance but want to maintain event handlers or focus behavior.
Set the monospace prop to render the input with a monospace font family. This is useful for code, identifiers, or technical values.
<Storybook.Demo> <Input defaultValue="Regular font" /> <Input monospace defaultValue="Monospace font" /> </Storybook.Demo>
<Input defaultValue="Regular font" />
<Input monospace defaultValue="Monospace font" />
The <Input> component uses the size prop for styling (controlling height and padding). To use the native HTML size attribute (which controls the number of characters the input should fit), use the nativeSize prop instead.
// Custom styling size (height/padding)
<Input size="sm" />
// Native size attribute (character width)
<Input nativeSize={20} />
// Both can be used together
<Input size="sm" nativeSize={10} />
Always pair inputs with labels for accessibility:
<label>
Email Address
<Input type="email" name="email" required />
</label>
For controlled inputs, manage the value in React state:
const [email, setEmail] = useState('');
<Input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
placeholder="[email protected]"
/>;
Indicate validation state using ARIA attributes and visual cues:
<Input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
aria-invalid={!isValid}
aria-describedby="email-error"
/>;
{
!isValid && <div id="email-error">Please enter a valid email address</div>;
}
The <Input> component is built on the native <input> element and automatically meets several WCAG 2.2 AA standards:
To ensure your input implementations are fully accessible, follow these guidelines:
Labels (WCAG 3.3.2)
<label> element with htmlFor, wrap the input in a <label>, or provide aria-label or aria-labelledby.// Good: Visible label
<label htmlFor="email">Email Address</label>
<Input id="email" type="email" />
// Good: Wrapping label
<label>
Email Address
<Input type="email" />
</label>
// Good: aria-label for icon-only or context-clear inputs
<Input type="search" aria-label="Search issues" />
Error Identification (WCAG 3.3.1)
aria-invalid="true" to programmatically indicate invalid inputsaria-describedby to associate error messages with inputs<Input
id="email"
type="email"
value={email}
aria-invalid={hasError}
aria-describedby={hasError ? 'email-error' : undefined}
/>;
{
hasError && (
<span id="email-error" role="alert">
Please enter a valid email address
</span>
);
}
Required Fields
required attribute for required fieldsPlaceholder Text
For more information, see the WAI-ARIA Text Input practices.