Input
View .mdAn input allows people to enter short, free form text data. If you need to collect more than one line of information, try textarea.
Default
Loading interactive example. Full source is available below.
View example source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { useId } from "@coreai-ds/react-components";
import { Label } from "@coreai-ds/react-components";
import { Input } from "@coreai-ds/react-components";
import * as React from 'react';
export const Default = () => {
const inputId = useId('input');
const styles = /* css */ `
.input-default-example {
display: flex;
flex-direction: column;
gap: var(--spacingVerticalXXS);
max-width: 400px;
}
`;
return (<>
<style>{styles}</style>
<div className="input-default-example">
<Label htmlFor={inputId}>Sample input</Label>
<Input id={inputId}/>
</div>
</>);
};
export default Default;
Resources
Best practices
Do
- Size the input to the approximate length of the expected content.
- Use placeholder text only as a short supplementary hint or formatting example.
- Associate placeholder text with an accessible label.
Don't
- Use placeholder text for essential information needed to complete the input.
- Rely on placeholder text instead of a persistent label or helper text.
- Add a period to placeholder text.
Behavior and states
- Inputs can be masked or use custom type attributes that affect their formatting.
- Input masking can work like placeholder text to help people enter data in a specific format.
- Inputs can appear standalone, as full-width block elements, or inline with text.
- The input width should fit the approximate length of the expected content.
- Put Input into Field if you need info buttons, helper text, and validation states.
API referenceGenerated CoreAI prop and type summaries
API
Generated CoreAI prop summaries, not replacement type declarations. Most native DOM/event and ARIA props are omitted. ? means optional; JSDoc shows defaults, deprecations, and conditional requirements. Defaults assume the standard CoreAI provider.
Input API
An Input component that extends Fluent UI's Input with an inline label.
interface InputProps {
/**
* Controls the colors and borders of the input.
*
* Note: 'filled-darker-shadow' and 'filled-lighter-shadow' are deprecated and will be removed in the future.
* @default 'outline'
*/
appearance?: "outline" | "filled-darker" | "filled-lighter" | "underline" | "filled-darker-shadow" | "filled-lighter-shadow";
as?: "input";
/**
* Input can't have children.
*/
children?: undefined;
className?: string;
/**
* Element after the input text, within the input border
*/
contentAfter?: WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null | undefined;
/**
* Element before the input text, within the input border
*/
contentBefore?: WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null | undefined;
/**
* Default value of the input. Provide this if the input should be an uncontrolled component
* which tracks its current state internally; otherwise, use `value`.
*
* (This prop is mutually exclusive with `value`.)
*/
defaultValue?: string;
/**
* Collapse the control to its content width when nothing is selected and
* grow it with the typed/selected content. Only available when `label` is set.
*/
dynamicWidth?: boolean;
/**
* The actual `<input>` element. `type="text"` will be automatically applied unless overridden.
*
* This is the "primary" slot, so native props specified directly on the `<Input>` will go here
* (except `className` and `style`, which go to the `root` slot). The top-level `ref` will
* also go here.
*/
input?: ({ as?: "input"; } & Omit<Omit<DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref"> & { ref?: ((instance: HTMLInputElement | null) => void) | RefObject<HTMLInputElement> | null; }, "children"> & { children?: undefined; }) | undefined;
/**
* Optional inline label rendered inside the control, before the value.
*/
label?: string;
/**
* Called when the user changes the input's value.
*/
onChange?: ((ev: ChangeEvent<HTMLInputElement>, data: InputOnChangeData) => void);
ref?: Ref<HTMLInputElement>;
/**
* Wrapper element which visually appears to be the input and is used for borders, focus styling, etc.
* (A wrapper is needed to properly position `contentBefore` and `contentAfter` relative to `input`.)
*
* The root slot receives the `className` and `style` specified directly on the `<Input>`.
* All other top-level native props will be applied to the primary slot, `input`.
*/
root?: NonNullable<WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null> | undefined;
/**
* Size of the input (changes the font size and spacing).
* @default 'medium'
*/
size?: "small" | "medium" | "large";
style?: CSSProperties;
/**
* Controls text overflow behavior for the value. When true, long text is truncated with an ellipsis.
*/
truncateTextOverflow?: boolean;
/**
* An input can have different text-based [types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input#input_types)
* based on the type of value the user will enter.
*
* Note that no custom styling is currently applied for alternative types, and some types may
* activate browser-default styling which does not match the Fluent design language.
*
* (For non-text-based types such as `button` or `checkbox`, use the appropriate component or an
* `<input>` element instead.)
* @default 'text'
*/
type?: "number" | "search" | "time" | "text" | "tel" | "url" | "email" | "date" | "datetime-local" | "month" | "password" | "week";
/**
* Current value of the input. Provide this if the input is a controlled component where you
* are maintaining its current state; otherwise, use `defaultValue`.
*
* (This prop is mutually exclusive with `defaultValue`.)
*/
value?: string;
}
Implementation
CoreAI 0.12.0; Fluent 9.74.9.
Import components from @coreai-ds/react-components. CoreAI guidance above takes precedence over API demonstration examples.
Appearance
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { useId } from "@coreai-ds/react-components";
import { Label } from "@coreai-ds/react-components";
import { Input } from "@coreai-ds/react-components";
import * as React from 'react';
// ---------- Appearance ----------
export const Appearance = () => {
const idPrefix = 'input-appearance-story';
const inputIds = {
filledDarker: useId(idPrefix),
outline: useId(idPrefix),
underline: useId(idPrefix),
filledLighter: useId(idPrefix),
};
const styles = /* css */ `
.input-appearance-example {
display: flex;
flex-direction: column;
max-width: 400px;
}
.input-appearance-example__field {
display: grid;
row-gap: var(--spacingVerticalXXS);
margin-top: var(--spacingVerticalL);
}
.input-appearance-example__field--filled-lighter {
margin-inline: -48px;
padding-block: var(--spacingVerticalXXL);
padding-inline: 48px;
background-color: var(--colorNeutralBackground3);
}
`;
return (<>
<style>{styles}</style>
<div className="input-appearance-example">
<div className="input-appearance-example__field">
<Label htmlFor={inputIds.outline}>Outline appearance</Label>
<Input appearance="outline" id={inputIds.outline}/>
</div>
<div className="input-appearance-example__field">
<Label htmlFor={inputIds.filledDarker}>Filled darker appearance (CoreAI default)</Label>
<Input appearance="filled-darker" id={inputIds.filledDarker}/>
</div>
<div className="input-appearance-example__field input-appearance-example__field--filled-lighter">
<Label htmlFor={inputIds.filledLighter}>Filled lighter appearance</Label>
<Input appearance="filled-lighter" id={inputIds.filledLighter}/>
</div>
<div className="input-appearance-example__field">
<Label htmlFor={inputIds.underline}>Underline appearance</Label>
<Input appearance="underline" id={inputIds.underline}/>
</div>
</div>
</>);
};
export default Appearance;
Content before/after
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { Label } from "@coreai-ds/react-components";
import { Input } from "@coreai-ds/react-components";
import { PersonRegular } from "@fluentui/react-icons";
import { Body1 } from "@coreai-ds/react-components";
import * as React from 'react';
import type { ButtonProps } from "@coreai-ds/react-components";
import { Button } from "@coreai-ds/react-components";
import { MicRegular } from "@fluentui/react-icons";
import { Text } from "@coreai-ds/react-components";
const useStyles = makeStyles({
root: {
display: 'flex',
flexDirection: 'column',
gap: '20px',
// Prevent the example from taking the full width of the page (optional)
maxWidth: '400px',
// Stack the label above the field (with 2px gap per the design system)
'> div': { display: 'flex', flexDirection: 'column', gap: '2px' },
},
});
const MicButton: React.FC<ButtonProps> = props => {
return <Button {...props} appearance="transparent" icon={<MicRegular />} size="small"/>;
};
export const ContentBeforeAfter = (): JSXElement => {
const styles = useStyles();
const beforeId = useId('content-before');
const afterId = useId('content-after');
const beforeAndAfterId = useId('content-before-and-after');
const beforeLabelId = useId('before-label');
const afterLabelId = useId('after-label');
return (<div className={styles.root}>
<div>
<Label htmlFor={beforeId}>Full name</Label>
<Input contentBefore={<PersonRegular />} id={beforeId}/>
<Body1>
An input with a decorative icon in the <code>contentBefore</code> slot.
</Body1>
</div>
<div>
<Label htmlFor={afterId}>First name</Label>
<Input contentAfter={<MicButton aria-label="Enter by voice"/>} id={afterId}/>
<Body1>
An input with a button in the <code>contentAfter</code> slot.
</Body1>
</div>
<div>
<Label htmlFor={beforeAndAfterId}>Amount to pay</Label>
<Input contentBefore={<Text size={400} id={beforeLabelId}>
$
</Text>} contentAfter={<Text size={400} id={afterLabelId}>
.00
</Text>} aria-labelledby={`${beforeAndAfterId} ${beforeLabelId} ${afterLabelId}`} id={beforeAndAfterId}/>
<Body1>
An input with a presentational value in the <code>contentBefore</code> slot and another presentational value
in the <code>contentAfter</code> slot.
</Body1>
</div>
</div>);
};
export default ContentBeforeAfter;
Controlled
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import * as React from 'react';
import { makeStyles } from "@coreai-ds/react-components";
import type { InputProps } from "@coreai-ds/react-components";
import { Label } from "@coreai-ds/react-components";
import { Input } from "@coreai-ds/react-components";
const useStyles = makeStyles({
root: {
display: 'flex',
flexDirection: 'column',
// Use 2px gap below the label (per the design system)
gap: '2px',
// Prevent the example from taking the full width of the page (optional)
maxWidth: '400px',
},
});
export const Controlled = (): JSXElement => {
const inputId = useId('input');
const [value, setValue] = React.useState('initial value');
const styles = useStyles();
const onChange: InputProps['onChange'] = (ev, data) => {
// The controlled input pattern can be used for other purposes besides validation,
// but validation is a useful example
if (data.value.length <= 20) {
setValue(data.value);
}
};
return (<div className={styles.root}>
<Label htmlFor={inputId}>Controlled input limiting the value to 20 characters</Label>
<Input value={value} onChange={onChange} id={inputId}/>
</div>);
};
export default Controlled;
Disabled
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { useId } from "@coreai-ds/react-components";
import * as React from 'react';
import { Label } from "@coreai-ds/react-components";
import { Input } from "@coreai-ds/react-components";
import { Switch } from "@coreai-ds/react-components";
// ---------- Disabled ----------
export const Disabled = () => {
const idPrefix = 'input-disabled-story';
const inputIds = {
default: useId(idPrefix),
filledDarker: useId(idPrefix),
filledLighter: useId(idPrefix),
underline: useId(idPrefix),
};
const [disabled, setDisabled] = React.useState(true);
const styles = /* css */ `
.input-disabled-example {
display: flex;
flex-direction: column;
max-width: 400px;
}
.input-disabled-example__field {
display: grid;
row-gap: var(--spacingVerticalXXS);
margin-top: var(--spacingVerticalL);
}
.input-disabled-example__field--filled-lighter {
margin-inline: -48px;
padding-block: var(--spacingVerticalXXL);
padding-inline: 48px;
background-color: var(--colorNeutralBackground3);
}
.input-disabled-example__toggle {
margin-top: var(--spacingVerticalXL);
}
`;
return (<>
<style>{styles}</style>
<div className="input-disabled-example">
<div className="input-disabled-example__field">
<Label htmlFor={inputIds.default}>Disabled (outline appearance)</Label>
<Input appearance="outline" disabled={disabled} id={inputIds.default} defaultValue="disabled value"/>
</div>
<div className="input-disabled-example__field">
<Label htmlFor={inputIds.filledDarker}>
Disabled (filled darker appearance, CoreAI default)
</Label>
<Input appearance="filled-darker" disabled={disabled} id={inputIds.filledDarker} defaultValue="disabled value"/>
</div>
<div className="input-disabled-example__field input-disabled-example__field--filled-lighter">
<Label htmlFor={inputIds.filledLighter}>Disabled (filled lighter appearance)</Label>
<Input appearance="filled-lighter" disabled={disabled} id={inputIds.filledLighter} defaultValue="disabled value"/>
</div>
<div className="input-disabled-example__field">
<Label htmlFor={inputIds.underline}>Disabled (underline appearance)</Label>
<Input appearance="underline" disabled={disabled} id={inputIds.underline} defaultValue="disabled value"/>
</div>
<Switch className="input-disabled-example__toggle" checked={disabled} label="Disabled" onChange={(_e, data) => setDisabled(data.checked)}/>
</div>
</>);
};
export default Disabled;
Inline
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { Label } from "@coreai-ds/react-components";
import { Input } from "@coreai-ds/react-components";
import * as React from 'react';
export const Inline = (): JSXElement => {
const inputId = useId('input');
return (<div>
<Label htmlFor={inputId} style={{ paddingInlineEnd: '12px' }}>
Sample inline input
</Label>
<Input id={inputId}/>
<p>
This input is <Input placeholder="inline" aria-label="inline"/> within a paragraph of text (be sure to provide
an <code>aria-label</code>).
</p>
</div>);
};
export default Inline;
Inline Label
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import * as React from 'react';
import { Input } from "@coreai-ds/react-components";
export const InlineLabel = () => {
const [name, setName] = React.useState<string>('');
const styles = /* css */ `
.input-inline-label-example {
display: flex;
flex-direction: column;
align-items: flex-start;
row-gap: var(--spacingVerticalL);
max-width: 320px;
padding: var(--spacingVerticalL);
}
.input-inline-label-example__field {
display: flex;
flex-direction: column;
align-items: flex-start;
row-gap: var(--spacingVerticalXS);
}
.input-inline-label-example__prop-label {
font-family: var(--fontFamilyMonospace);
font-size: var(--fontSizeBase200);
color: var(--colorNeutralForeground3);
}
`;
return (<>
<style>{styles}</style>
<div className="input-inline-label-example">
<div className="input-inline-label-example__field">
<code className="input-inline-label-example__prop-label">label</code>
<Input label="Name" placeholder="Enter a name" value={name} onChange={(_event, data) => setName(data.value)}/>
</div>
<div className="input-inline-label-example__field">
<code className="input-inline-label-example__prop-label">label, dynamicWidth</code>
<Input label="Name" dynamicWidth placeholder="Enter a name"/>
</div>
</div>
</>);
};
export default InlineLabel;
Placeholder
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { Label } from "@coreai-ds/react-components";
import { Input } from "@coreai-ds/react-components";
import * as React from 'react';
const useStyles = makeStyles({
root: {
display: 'flex',
flexDirection: 'column',
rowGap: '5px',
maxWidth: '300px',
},
});
export const Placeholder = (): JSXElement => {
const inputId = useId('input-with-placeholder');
const styles = useStyles();
return (<div className={styles.root}>
<Label htmlFor={inputId}>Input with a placeholder</Label>
<Input placeholder="This is a placeholder" id={inputId}/>
</div>);
};
export default Placeholder;
Read Only
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { useId } from "@coreai-ds/react-components";
import { Label } from "@coreai-ds/react-components";
import { Input } from "@coreai-ds/react-components";
import * as React from 'react';
export const ReadOnly = () => {
const idPrefix = 'input-readonly-story';
const inputIds = {
token: useId(idPrefix),
editable: useId(idPrefix),
};
const styles = /* css */ `
.input-read-only-example {
display: flex;
flex-direction: column;
max-width: 400px;
}
.input-read-only-example__field {
display: grid;
row-gap: var(--spacingVerticalXXS);
margin-top: var(--spacingVerticalL);
}
`;
return (<>
<style>{styles}</style>
<div className="input-read-only-example">
<div className="input-read-only-example__field">
<Label htmlFor={inputIds.token}>Read-only (with copy button)</Label>
<Input readOnly id={inputIds.token} value="sk-9f3c1a7b2e4d6f80"/>
</div>
<div className="input-read-only-example__field">
<Input readOnly label="API key" value="sk-9f3c1a7b2e4d6f80"/>
</div>
<div className="input-read-only-example__field">
<Label htmlFor={inputIds.editable}>Editable (no copy button)</Label>
<Input id={inputIds.editable} defaultValue="Type to edit me"/>
</div>
</div>
</>);
};
export default ReadOnly;
Size
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { Label } from "@coreai-ds/react-components";
import { Input } from "@coreai-ds/react-components";
import * as React from 'react';
const useStyles = makeStyles({
root: {
display: 'flex',
flexDirection: 'column',
gap: '20px',
maxWidth: '400px',
// Stack the label above the field (with 2px gap per the design system)
'> div': { display: 'flex', flexDirection: 'column', gap: '2px' },
},
});
export const Size = (): JSXElement => {
const smallId = useId('input-small');
const mediumId = useId('input-medium');
const largeId = useId('input-large');
const styles = useStyles();
return (<div className={styles.root}>
<div>
<Label size="small" htmlFor={smallId}>
Small input
</Label>
<Input size="small" id={smallId}/>
</div>
<div>
<Label size="medium" htmlFor={mediumId}>
Medium input
</Label>
<Input size="medium" id={mediumId}/>
</div>
<div>
<Label size="large" htmlFor={largeId}>
Large input
</Label>
<Input size="large" id={largeId}/>
</div>
</div>);
};
export default Size;
Type
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { Label } from "@coreai-ds/react-components";
import { Input } from "@coreai-ds/react-components";
import * as React from 'react';
const useStyles = makeStyles({
root: {
display: 'flex',
flexDirection: 'column',
gap: '20px',
maxWidth: '400px',
// Stack the label above the field (with 2px gap per the design system)
'> div': { display: 'flex', flexDirection: 'column', gap: '2px' },
},
});
export const Type = (): JSXElement => {
const emailId = useId('input-email');
const urlId = useId('input-url');
const passwordId = useId('input-password');
const styles = useStyles();
return (<form noValidate autoComplete="off" className={styles.root}>
<div>
<Label htmlFor={emailId}>Email input</Label>
<Input type="email" id={emailId}/>
</div>
<div>
<Label htmlFor={urlId}>URL input</Label>
<Input type="url" id={urlId}/>
</div>
<div>
<Label htmlFor={passwordId}>Password input</Label>
<Input type="password" defaultValue="password" id={passwordId}/>
</div>
</form>);
};
export default Type;
Uncontrolled
Loading interactive example. Full source is available below.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { Label } from "@coreai-ds/react-components";
import { Input } from "@coreai-ds/react-components";
import type { InputProps } from "@coreai-ds/react-components";
import * as React from 'react';
const useStyles = makeStyles({
root: {
// Stack the label above the field
display: 'flex',
flexDirection: 'column',
// Use 2px gap below the label (per the design system)
gap: '2px',
// Prevent the example from taking the full width of the page (optional)
maxWidth: '400px',
},
});
const onChange: InputProps['onChange'] = (ev, data) => {
// Uncontrolled inputs can be notified of changes to the value
console.log(`New value: "${data.value}"`);
};
export const Uncontrolled = (): JSXElement => {
const inputId = useId('input');
const styles = useStyles();
return (<div className={styles.root}>
<Label htmlFor={inputId}>Uncontrolled input with default value</Label>
<Input defaultValue="default value" onChange={onChange} id={inputId}/>
</div>);
};
export default Uncontrolled;