Text Input
Text Inputs allow users to enter words, numbers, or characters without styling.
Anatomy

- Label: Title of the Text Input.
- Input Container: Rectangular container that houses the placeholder and input text.
- Placeholder/Input Text: Placeholder text is optional and shows an example of how to format the text for what the input is used for.
Usage Guidance
- Text Inputs can only support words, numbers or characters.
- Standard and Wide Text Inputs does not support images or any text styling.
- To ensure we don’t overwhelm users, there shouldn’t be more than two Wide Text Inputs on a page.
- For all Text Inputs on Web, a user clicking into an input or label that is not disabled will trigger the text cursor to appear, allowing users the ability to type. As the user types in the Text Input, the placeholder text is replaced with the user’s input.
When to Use
- Text Input is typically a form element used to collect user data that includes words, numbers or characters.
When to Use Something Else
- If styling is needed, such as for configuring email messages, you can use a Rich Text Editor instead.
- Use a Text Area when you need to let users enter an amount of text that’s longer than a single line.
- Consider using a Select, Radio or Checkboxes if there are predetermined data that a user should not input themselves.
Examples
Basic Example
Text Input should be used in tandem with Form Field to ensure proper label association and screen reader support.
import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {TextInput} from '@workday/canvas-kit-react/text-input';
export default () => {
const [value, setValue] = React.useState('');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
return (
<FormField>
<FormField.Label>Email</FormField.Label>
<FormField.Field>
<FormField.Input as={TextInput} onChange={handleChange} value={value} />
</FormField.Field>
</FormField>
);
};
Disabled
Set the disabled prop of the Text Input to prevent users from interacting with it.
import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {TextInput} from '@workday/canvas-kit-react/text-input';
export default () => {
const [value, setValue] = React.useState('');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
return (
<FormField>
<FormField.Label>Email</FormField.Label>
<FormField.Field>
<FormField.Input as={TextInput} disabled onChange={handleChange} value={value} />
</FormField.Field>
</FormField>
);
};
Placeholder
Set the placeholder prop of the Text Input to display a sample of its expected format or value
before a value has been provided.
import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {TextInput} from '@workday/canvas-kit-react/text-input';
export default () => {
const [value, setValue] = React.useState('');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
return (
<FormField>
<FormField.Label>Email</FormField.Label>
<FormField.Field>
<FormField.Input
as={TextInput}
onChange={handleChange}
placeholder="user@email.com"
value={value}
/>
</FormField.Field>
</FormField>
);
};
Accessibility Note: Always provide a persistent
FormField.Labeland never rely on placeholder text as the only label for an input. Placeholders can disappear or lack sufficient contrast. Use placeholders only for short format examples (e.g., “name@example.com”), and place detailed instructions or guidance inFormField.Hintinstead of the placeholder.
Ref Forwarding
Text Input supports ref forwarding. It will forward
ref to its underlying <input type="text"> element.
import React from 'react';
import {PrimaryButton} from '@workday/canvas-kit-react/button';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {TextInput} from '@workday/canvas-kit-react/text-input';
export default () => {
const [value, setValue] = React.useState('');
const ref = React.useRef(null);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
const handleClick = () => {
ref.current.focus();
};
return (
<>
<FormField>
<FormField.Label>Email</FormField.Label>
<FormField.Field>
<FormField.Input as={TextInput} onChange={handleChange} ref={ref} value={value} />
</FormField.Field>
</FormField>
<PrimaryButton onClick={handleClick}>Focus Text Input</PrimaryButton>
</>
);
};
Grow
Set the grow prop of the wrapping Form Field to true to configure the Text Input to expand to
the width of its container.
import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {TextInput} from '@workday/canvas-kit-react/text-input';
export default () => {
const [value, setValue] = React.useState('');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
return (
<FormField grow>
<FormField.Label>Street Address</FormField.Label>
<FormField.Field>
<FormField.Input as={TextInput} onChange={handleChange} value={value} />
</FormField.Field>
</FormField>
);
};
The grow prop may also be applied directly to the Text Input if Form Field is not being used.
Label Position Horizontal
Set the orientation prop of the Form Field to designate the position of the label relative to the
input component. By default, the orientation will be set to vertical.
Add a valid email
import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {TextInput} from '@workday/canvas-kit-react/text-input';
export default () => {
const [value, setValue] = React.useState('');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
return (
<FormField orientation="horizontalStart">
<FormField.Label>Email</FormField.Label>
<FormField.Field>
<FormField.Input as={TextInput} onChange={handleChange} value={value} />
<FormField.Hint>Add a valid email</FormField.Hint>
</FormField.Field>
</FormField>
);
};
Required
Set the required prop of the wrapping Form Field to true to indicate that the field is required.
Labels for required fields are suffixed by a red asterisk.
import React from 'react';
import {FormField} from '@workday/canvas-kit-react/form-field';
import {TextInput} from '@workday/canvas-kit-react/text-input';
export default () => {
const [value, setValue] = React.useState('');
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
return (
<FormField isRequired={true}>
<FormField.Label>Email</FormField.Label>
<FormField.Field>
<FormField.Input as={TextInput} onChange={handleChange} value={value} />
</FormField.Field>
</FormField>
);
};
Icons
InputGroup is available to add icons to the TextInput. Internally, a container div element is
used with relative position styling on the div and absolute position styling on the start and end
icons. InputGroup.InnerStart and InputGroup.InnerEnd are used to position elements at the start
and end of the input. “start” and “end” are used instead of “left” and “right” to match
CSS Logical Properties
and will be semantically correct in left-to-right and right-to-left languages.
InputGroup.InnerStart and InputGroup.InnerEnd subcomponents can handle any child elements, but
are built for icons. The default width is 40px, which is perfect for icons. If you need to use
something else, be sure to set the width property of InputGroup.InnerStart or
InputGroup.InnerEnd to match the intended width of the element. Do not use the cs prop or any
method to change width. The width prop is used to correctly position other inner elements.
import React from 'react';
import {mailIcon} from '@workday/canvas-system-icons-web';
import {
FormField,
useFormFieldModel,
useFormFieldInput,
} from '@workday/canvas-kit-react/form-field';
import {InputGroup} from '@workday/canvas-kit-react/text-input';
import {SystemIcon} from '@workday/canvas-kit-react/icon';
/**
* Using `as={InputGroup}` on `FormField.Input` will break the label associations necessary for accessibility.
* In this example, we've rendered `FormField.Field` as `InputGroup` and then hoisted the `id` of the input from the FormField model.
* This allows us to set the `id` of the `InputGroup.Input` correctly for proper label association.
*/
export default () => {
const model = useFormFieldModel();
const {id: formFieldInputId} = useFormFieldInput(model);
return (
<FormField model={model}>
<FormField.Label>Email</FormField.Label>
<FormField.Field as={InputGroup}>
<InputGroup.InnerStart>
<SystemIcon icon={mailIcon} size="small" />
</InputGroup.InnerStart>
<InputGroup.Input id={formFieldInputId} autoComplete="email" />
<InputGroup.InnerEnd>
<InputGroup.ClearButton />
</InputGroup.InnerEnd>
</FormField.Field>
</FormField>
);
};
Accessibility Note: In this example, the mail icon is decorative and hidden from screen readers. If icons are used for conveying meaning in addition to the label text, a text alternative must be provided for screen readers.
Error States
Form Field provides error and caution states for Text Input. Set the error prop on Form Field to
"error" or "caution" and use FormField.Hint to provide error messages. See
Form Field’s Error documentation for examples and
accessibility guidance.
Accessibility
TextInput should be used with Form Field to ensure proper
labeling, error handling, and help text association. See
FormField’s accessibility documentation for
comprehensive guidance on form accessibility best practices.
Autocomplete Attribute
- Add appropriate
autoCompletevalues to indicate the input’s purpose (e.g.,"email","name","street-address","tel"). Read more about Identify Input Purpose. - Autocomplete enables browser autofill and helps assistive technologies understand the field’s purpose, benefiting users with cognitive disabilities and motor impairments.
- Autocomplete also helps password managers identify the correct fields.
Input Type for Mobile Keyboards
TextInput defaults to <input type="text">, but for better mobile keyboard support, use more
specific type attributes (like "email", "tel", "url", or "search") as needed.
Screen Reader Experience
When properly implemented with FormField, screen readers will announce:
- The label text when the input receives focus.
- Required, disabled, or read-only status.
- Help text and error messages (via
aria-describedby). - The current value or “blank” if empty.
Component API
TextInput
Props
Props extend from input. Changing the as prop will change the element interface.
| Name | Type | Description | Default |
|---|---|---|---|
error | | The type of error associated with the TextInput (if applicable). | |
width | number | string | The width of the TextInput. | |
grow | boolean | True if the component should grow to its container's width. False otherwise. | |
cs | | The | |
children | React.ReactNode | ||
as | React.ElementType | Optional override of the default element used by the component. Any valid tag or Component. If you provided a Component, this component should forward the ref using Note: Not all elements make sense and some elements may cause accessibility issues. Change this value with care. | input |
ref | React.Ref<R = input> | Optional ref. If the component represents an element, this ref will be a reference to the real DOM element of the component. If |
TextInput.ErrorType
Basic type information:
ErrorTypeInputGroup
An InputGroup is a container around a with optional inner start and end
elements. The inner start and end elements are usually icons or icon buttons visually represented
inside the input. The InputGroup will add padding to the input so the icons/buttons display
correctly. This component uses React.Children.map and React.cloneElement from the
React.Children API. This means all children must be
InputGroup.* components. Any other direct children will cause issues. You can add different
elements/components inside the and
subcomponents.
<InputGroup>
<InputGroup.InnerStart as={SystemIcon} pointerEvents="none" icon={searchIcon} />
<InputGroup.Input />
<InputGroup.InnerEnd>
<TertiaryButton tabIndex={-1} icon={xIcon} size="small" />
</InputGroup.InnerEnd>
</InputGroup>
Layout Component
InputGroup supports all props from thelayout component.
Props
Props extend from div. Changing the as prop will change the element interface.
Props extend from . If a model is passed, props from InputGroupModelConfig are ignored.
| Name | Type | Description | Default |
|---|---|---|---|
cs | | The | |
children | ReactNode | ||
as | React.ElementType | Optional override of the default element used by the component. Any valid tag or Component. If you provided a Component, this component should forward the ref using Note: Not all elements make sense and some elements may cause accessibility issues. Change this value with care. | div |
ref | React.Ref<R = div> | Optional ref. If the component represents an element, this ref will be a reference to the real DOM element of the component. If | |
model | | Optional model to pass to the component. This will override the default model created for the component. This can be useful if you want to access to the state and events of the model, or if you have nested components of the same type and you need to override the model provided by React Context. | |
elemPropsHook | ( | Optional hook that receives the model and all props to be applied to the element. If you use this, it is your responsibility to return props, merging as appropriate. For example, returning an empty object will disable all elemProps hooks associated with this component. This allows finer control over a component without creating a new one. |
InputGroup.InnerStart
Basic type information:
InputGroupInnerStartInputGroup.Input
Basic type information:
InputGroupInputInputGroup.InnerEnd
Basic type information:
InputGroupInnerEndInputGroup.ClearButton
Basic type information:
ClearButtonModel
useInputGroupModel
useInputGroupModel (config: ): Specifications
Content Guidelines
- Labels for Text Inputs are written in title case.
- Refer to the guidelines on Placeholder Text in the Content Style Guide for more tips on how to write placeholder text.
Anatomy

- Label: Text used as a title or brief description of the input.
- Placeholder/Input Text: Placeholder text is optional and shows an example of the intended input.
- Helper Text (Optional): Message to aid in users understanding of acceptable input text.
- Input Container: Rectangular container that houses the placeholder and input text.
- Required Asterisk (Optional): Appears only when the input is required.
Interaction States
Mobile inputs include several interactive states; inactive, active, disabled and pressed states.

Inactive states are the default state if the input is not active, disabled or displaying an alert.

Active states are displayed with a blue border, a cursor, and a keyboard.

Disabled states are grayed out to prevent users from interacting with the input.

While actively pressing on an input, the container fill is displayed in gray to provide feedback to the user of their touch.
Notification States
Notification states on mobile include an additional visual affordance compared to web notifications. They include an icon and a notification label.

Usage Guidance
When to Use
- A Text Input is used to collect user data that includes words, numbers, or characters.
- Use helper text to instruct the user what the proper input type may be.
- Use placeholder text only to provide examples of proper inputs. Never use placeholder text to help instruct users.
When to Use Something Else
- If the user needs to input a time, use a time picker.
- If the user needs to input a date, use a date picker.
- If input styling is needed, such as for configuring email messages, use a Rich Text Editor.
- Use a Text Area when you need to let users enter an amount of text that’s longer than a single line.
- Consider using a Select, Radio or Checkboxes if there is predetermined data that a user should not input themself.

Do
Do use helper text to display important information that aids in the successful completion of an input.

Don't
Don't use instructional text as placeholder text because it will disappear as the user starts typing and will no longer be helpful to the user.
Mobile Guidance
- Mobile text inputs are larger in height to better accommodate finger interaction.
- Mobile text inputs have larger typography for better readability on smaller screens.
Truncation
Labels
Labels should be short and concise. They do not have a limit on how long they may be. Once the label exceeds the length of the container it will wrap to another line.
Helper Text
Helper Text should be short and concise. They do not have a limit on how long they may be. Once helper text exceeds the length of the container it will wrap to another line.
Input Text
When the input text exceeds the length of the container the text becomes truncated at the trailing end of the container while inactive. While actively typing into the input, the text scrolls off the leading edge of the container.
Keyboards
Mobile devices utilize on-screen keyboards. It is often up to the designer to specify keyboard-type in their designs for the intended input. These keyboards vary based on the brand of the device; however, most devices have dedicated keyboards for specific types of inputs such as:
- Alphabetic
- Numeric
- Alphanumeric

Do
Use the appropriate keyboard based on the intended input.

Don't
Don't use the wrong keyboard for an input as this causes more work for the user. This could prevent the user from completing their task.
Screen Size Differences
Typically, inputs on mobile should stretch to the full width of the screen. However, there are some exceptions for providing limited space to set expectations of the input, such as providing a smaller text input box for entering an address ZIP code. For larger mobile devices, inputs should have a maximum width of 600 pts to retain proper readability.
API Guidelines
LabelledTextField
public struct LabelledTextField: ViewMethods
public init(
featureData: FeatureMetricsData,
text: Binding<String>,
placeholderText: String? = nil,
label: String,
accessibilityLabel: String,
textFieldButtonAccessibilityLabel: String,
helperText: String? = nil,
context: Binding<SemanticContext>,
isSecure: Bool = false,
keyboardType: UIKeyboardType = .default,
onFocusChanged: ((Bool) -> Void)? = nil,
onSubmission: (() -> Void)? = nil
)Create an instance of LabelledTextField.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| featureData | FeatureMetricsData | The feature name/context and the screen ID in which the component appears | |
| text | Binding<String> | Text binding so the clear button can work, already localized. | |
| placeholderText | String? | nil | Label for possible content of the field, already localized. Default is nil. |
| label | String | Label to be positioned above field, already localized. | |
| accessibilityLabel | String | Accessibility label of field, already localized. | |
| textFieldButtonAccessibilityLabel | String | Accessibility label of the text field button (re: “x” clear button). | |
| helperText | String? | nil | Helper text below field, already localized. Default is nil. |
| localizer | LocalizationAdapting | Localization provider | |
| context | SemanticContext | SemanticContext of the field. | |
| isSecure | Bool | false | Optional bool which allows you to use a SecureField instead of a TextField. Default is false. |
| keyboardType | UIKeyboardType | default | Optional UIKeyboardType which allows you to change the keyboard type used with the TextField. Default is .default. |
| onFocusChanged | ((Bool) -> Void)? | nil | Optional closure which allows you to listen to focus changes from the TextField or SecureField. Default is nil. |
| onSubmission | (() -> Void)? | nil | Optional closure which allows you to listen to the onSubmit action from the TextField or SecureField. Default is nil. |
Anatomy

- Label: Text used as a title or brief description of the input.
- Placeholder/Input Text: Placeholder text is optional and shows an example of the intended input.
- Helper Text (Optional): Message to aid in users understanding of acceptable input text.
- Input Container: Rectangular container that houses the placeholder and input text.
- Required Asterisk (Optional): Appears only when the input is required.
Interaction States
Mobile inputs include several interactive states; inactive, active, disabled and pressed states.

Inactive states are the default state if the input is not active, disabled or displaying an alert.

Active states are displayed with a blue border, a cursor, and a keyboard.

Disabled states are grayed out to prevent users from interacting with the input.

While actively pressing on an input, the container fill is displayed in gray to provide feedback to the user of their touch.
Notification States
Notification states on mobile include an additional visual affordance compared to web notifications. They include an icon and a notification label.

Usage Guidance
When to Use
- A Text Input is used to collect user data that includes words, numbers, or characters.
- Use helper text to instruct the user what the proper input type may be.
- Use placeholder text only to provide examples of proper inputs. Never use placeholder text to help instruct users.
When to Use Something Else
- If the user needs to input a time, use a time picker.
- If the user needs to input a date, use a date picker.
- If input styling is needed, such as for configuring email messages, use a Rich Text Editor.
- Use a Text Area when you need to let users enter an amount of text that’s longer than a single line.
- Consider using a Select, Radio or Checkboxes if there is predetermined data that a user should not input themself.

Do
Do use helper text to display important information that aids in the successful completion of an input.

Don't
Don't use instructional text as placeholder text because it will disappear as the user starts typing and will no longer be helpful to the user.
Mobile Guidance
- Mobile text inputs are larger in height to better accommodate finger interaction.
- Mobile text inputs have larger typography for better readability on smaller screens.
Truncation
Labels
Labels should be short and concise. They do not have a limit on how long they may be. Once the label exceeds the length of the container it will wrap to another line.
Helper Text
Helper Text should be short and concise. They do not have a limit on how long they may be. Once helper text exceeds the length of the container it will wrap to another line.
Input Text
When the input text exceeds the length of the container the text becomes truncated at the trailing end of the container while inactive. While actively typing into the input, the text scrolls off the leading edge of the container.
Keyboards
Mobile devices utilize on-screen keyboards. It is often up to the designer to specify keyboard-type in their designs for the intended input. These keyboards vary based on the brand of the device; however, most devices have dedicated keyboards for specific types of inputs such as:
- Alphabetic
- Numeric
- Alphanumeric

Do
Use the appropriate keyboard based on the intended input.

Don't
Don't use the wrong keyboard for an input as this causes more work for the user. This could prevent the user from completing their task.
Screen Size Differences
Typically, inputs on mobile should stretch to the full width of the screen. However, there are some exceptions for providing limited space to set expectations of the input, such as providing a smaller text input box for entering an address ZIP code. For larger mobile devices, inputs should have a maximum width of 600 pts to retain proper readability.
API Guidelines
Component Definition
@Composable
fun TextInputUiComponent(
modifier: Modifier = Modifier,
label: String? = null,
value: String,
onValueChange: (String) -> Unit,
readOnly: Boolean = false,
placeholder: String? = null,
placeholderTextStyle: TextStyle = WorkdayTheme.canvasTypography.bodyMedium,
heightConfig: TextInputHeightConfig = singleLineConfig(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
leadingIcon: @Composable (() -> Unit)? = null,
trailingIcon: @Composable (() -> Unit)? = null,
helperText: String? = null,
onClickInputText: (() -> Unit) = {},
onClickClearTextIcon: (() -> Unit) = {},
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions(),
semanticState: SemanticState = SemanticState()
) {Parameters
All undocumented parameters are native Android Compose types that can be found in the developer documentation.
| Name | Type | Default | Description |
|---|---|---|---|
| label | String? | null | The text above the input that typically designates the name of the component |
| value | String | The text to be displayed inside of the text input | |
| onValueChange | (String) -> Unit) | Callback lambda that is executed whenever the text of the input changes. | |
| readOnly | Boolean | false | Removes the ability for user input into the text input. The entire Input surface provides a ripple click animation when readOnly is set to true. |
| placeholder | String? | null | This text is shown inside of the input when the value is empty to provide a hint to the user. |
| placeholderTextStyle | TextStyle | bodySmall | Configures the placeholder typography style. |
| heightConfig | TextInputHeightConfig | singleLineConfig() | Toggles between a TextInput and TextArea UI Component. Provides min height and fixed height configurations for multi-line inputs. |
| leadingIcon | Composable? | null | Icon composable rendered at the start of the input area. |
| trailingIcon | Composable? | null | Icon composable rendered at the end of the input area. |
| helperText | String? | null | Helper text is displayed underneath the input area that to give additional context to the user. Error and Alert helper text prefixes are localized and automatically displayed when the component’s semantic state is changed |
| onClickInputText | (() -> Unit) | Callback lambda that is executed when the user clicks on the Input area. | |
| onClickClearTextIcon | (() -> Unit) | Callback lambda that is executed when the user clicks on the “clear text” trailing Icon that is displayed while the component is focused. | |
| semanticState | SemanticState | SemanticState() | Adjusts the state of the Component. This allows for enabling, disabling, warning, error, and required states. |
Can't Find What You Need?
Check out our FAQ section which may help you find the information you're looking for. For further information, contact the #ask-canvas-design or #ask-canvas-kitchannels on Slack.