Skip to main content
react-md

Text Field

Text fields allow users to enter text into a UI. They typically appear in forms and dialogs.

Simple Text Field

The default text field will look similar to a native <input type="text"> with a border surrounding the text content that will change color and thickness when hovered or focused. Every text field should have an accessible label by providing one of the following props: label, aria-label, aria-labelledby.

It is generally recommended to place a label with the text field, but placeholder only text fields are also supported.

import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { TextField } from "@react-md/core/form/TextField";
import { type ReactElement } from "react";

export default function OutlinedTextField(): ReactElement {
  return (
    <Form className={box({ stacked: true, align: "stretch" })}>
      <TextField aria-label="Label" placeholder="Placeholder-only" />
      <TextField label="Label" placeholder="Placeholder" />
    </Form>
  );
}

Press Enter to start editing.

Filled Text Field

Filled text fields have more visual emphasis than outlined text fields, making them stand out when surrounded by other content and components.

import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { TextField } from "@react-md/core/form/TextField";
import { type ReactElement } from "react";

export default function FilledTextField(): ReactElement {
  return (
    <Form className={box({ stacked: true, align: "stretch" })}>
      <TextField
        aria-label="Label"
        placeholder="Placeholder-only"
        theme="filled"
      />
      <TextField label="Label" placeholder="Placeholder" theme="filled" />
      <TextField
        label="Label"
        placeholder="Placeholder"
        theme="filled"
        underlineDirection="center"
      />
      <TextField
        label="Label"
        placeholder="Placeholder"
        theme="filled"
        underlineDirection="right"
      />
    </Form>
  );
}

Press Enter to start editing.

Underlined Text Field

Underlined text fields have the least emphasis, making them better for less prominent editing.

import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { TextField } from "@react-md/core/form/TextField";
import { type ReactElement } from "react";

export default function UnderlinedTextField(): ReactElement {
  return (
    <Form className={box({ stacked: true, align: "stretch" })}>
      <TextField
        aria-label="Label"
        placeholder="Placeholder-only"
        theme="underline"
      />
      <TextField label="Label" placeholder="Placeholder" theme="underline" />
      <TextField
        label="Label"
        placeholder="Placeholder"
        theme="underline"
        underlineDirection="center"
      />
      <TextField
        label="Label"
        placeholder="Placeholder"
        theme="underline"
        underlineDirection="right"
      />
    </Form>
  );
}

Press Enter to start editing.

No Theme Text Field

The text field can also be set with theme="none" which removes most of the styling behavior. There isn't much use for this though since the floating label will not display correctly when this is set and can only be used for placeholder only text fields.

import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { TextField } from "@react-md/core/form/TextField";
import { type ReactElement } from "react";

export default function NoThemeTextField(): ReactElement {
  return (
    <Form className={box({ stacked: true, align: "stretch" })}>
      <TextField
        aria-label="Label"
        placeholder="Placeholder-only"
        theme="none"
      />
    </Form>
  );
}

Press Enter to start editing.

Text Field State

The text field supports the following states:

Form Theme

"use client";

import { Box } from "@react-md/core/box/Box";
import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { Radio } from "@react-md/core/form/Radio";
import { TextField } from "@react-md/core/form/TextField";
import { type FormTheme } from "@react-md/core/form/types";
import { useRadioGroup } from "@react-md/core/form/useRadioGroup";
import { Typography } from "@react-md/core/typography/Typography";
import { typography } from "@react-md/core/typography/typographyStyles";
import { type ReactElement } from "react";

export default function TextFieldState(): ReactElement {
  const { value: theme, getRadioProps } = useRadioGroup<FormTheme>({
    name: "theme",
    defaultValue: "outline",
  });
  return (
    <Form className={box({ stacked: true, align: "stretch" })}>
      <TextField label="Disabled" theme={theme} disabled />
      <TextField
        label="Read Only"
        theme={theme}
        readOnly
        defaultValue="Some text to display"
      />
      <TextField label="Active" theme={theme} active />
      <TextField label="Error" theme={theme} error />
      <TextField label="Normal" theme={theme} />
      <Box stacked disablePadding align="start" fullWidth>
        <Typography>Form Theme</Typography>
        {themes.map((theme) => (
          <Radio
            key={theme}
            {...getRadioProps(theme)}
            label={theme}
            className={typography({ type: null, textTransform: "capitalize" })}
          />
        ))}
      </Box>
    </Form>
  );
}

const themes: readonly FormTheme[] = ["underline", "filled", "outline"];

Press Enter to start editing.

Text Field Type

The text field supports rendering as most of the input types that are displayed as a textbox. There is no additional functionality built-in to support these types other than attempting to display them correctly.

import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { TextField } from "@react-md/core/form/TextField";
import { type ReactElement } from "react";

export default function TextFieldType(): ReactElement {
  return (
    <Form className={box({ stacked: true, fullWidth: true, align: "start" })}>
      <TextField label="Text" type="text" />
      <TextField label="Password" type="password" />
      <TextField label="Number" type="number" />
      <TextField label="Tel" type="tel" />
      <TextField label="Email" type="email" />
      <TextField label="Date" type="date" />
      <TextField label="Time" type="time" />
      <TextField label="Datetime-local" type="datetime-local" />
      <TextField label="Url" type="url" />
      <TextField label="Color" type="color" />
      <TextField label="Search" type="search" />
    </Form>
  );
}

Press Enter to start editing.

Password

When rendering password fields, it is recommended to use the Password component instead which will allow the user to toggle the visibility of the password.

import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { Password } from "@react-md/core/form/Password";
import { type ReactElement } from "react";

export default function PasswordExample(): ReactElement {
  return (
    <Form className={box({ stacked: true, align: "stretch" })}>
      <Password label="Password" theme="outline" />
      <Password label="Password" theme="filled" />
      <Password label="Password" theme="underline" />
    </Form>
  );
}

Press Enter to start editing.

Number

The default browser implementation <input type="number"> leaves much to be desired so a useNumberField hook is provided to fix the following issues and additional type safety.

See more info at the useNumberField documentation.

The current value is:

undefined

"use client";

/* eslint-disable @typescript-eslint/no-unused-vars */
import { Button } from "@react-md/core/button/Button";
import { Form } from "@react-md/core/form/Form";
import { TextField } from "@react-md/core/form/TextField";
import { useNumberField } from "@react-md/core/form/useNumberField";
import { Typography } from "@react-md/core/typography/Typography";
import { type ReactElement } from "react";

export default function NumberExample(): ReactElement {
  const { value, error, errorMessage, fieldProps, fieldRef, reset, setState } =
    useNumberField({
      name: "numberExample",
    });

  return (
    <Form onReset={reset}>
      <Typography margin="none">The current value is:</Typography>
      <Typography margin="bottom">{`${value}`}</Typography>
      <TextField label="Number" {...fieldProps} />
      <Button type="reset" theme="warning" themeType="outline">
        Reset
      </Button>
    </Form>
  );
}

Press Enter to start editing.

Simple TextArea

Use the TextArea component when multiple lines of text should be supported. It supports all the same theming, addons, help text, and error text as the TextField but renders in a <textarea> instead.

The default behavior of the TextArea is to resize it's height based on the current amount of text.

import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { TextArea } from "@react-md/core/form/TextArea";
import { type ReactElement } from "react";

export default function SimpleTextArea(): ReactElement {
  return (
    <Form className={box({ stacked: true, align: "stretch" })}>
      <TextArea label="Label" placeholder="Placeholder..." />
      <TextArea label="Label" placeholder="Placeholder..." theme="filled" />
      <TextArea label="Label" placeholder="Placeholder..." theme="underline" />
    </Form>
  );
}

Press Enter to start editing.

Setting Max Rows

Since it might not be ideal to support an infinite editing height, the max number of rows to grow to can be specified by the maxRows prop.

import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { TextArea } from "@react-md/core/form/TextArea";
import { type ReactElement } from "react";

export default function ResizingTextAreaMaxRows(): ReactElement {
  return (
    <Form className={box({ stacked: true, align: "stretch" })}>
      <TextArea
        label="Label"
        placeholder="Placeholder..."
        maxRows={8}
        defaultValue={LOREM_IPSUM}
      />
      <TextArea
        label="Label"
        placeholder="Placeholder..."
        maxRows={8}
        theme="filled"
        defaultValue={LOREM_IPSUM}
      />
      <TextArea
        label="Label"
        placeholder="Placeholder..."
        maxRows={8}
        theme="underline"
        defaultValue={LOREM_IPSUM}
      />
    </Form>
  );
}

const LOREM_IPSUM = `Duis vehicula risus quis urna varius ultrices. Cras id ipsum sed mauris sollicitudin feugiat at eget erat. Sed sed magna sed risus ornare ullamcorper. Curabitur vehicula lorem ante, vel facilisis nunc pulvinar quis. Duis gravida, purus at consequat scelerisque, libero est eleifend sem, ac aliquet dolor ex vitae arcu. Nam at dignissim orci. Nulla posuere sollicitudin malesuada. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nullam at libero mi. Ut a lorem at leo euismod ultricies.

Etiam nisi tellus, accumsan ut leo vel, iaculis feugiat ligula. Nullam congue lorem non lorem maximus porta. Pellentesque nunc magna, tincidunt consectetur maximus vel, euismod in ligula. Duis quis metus ligula. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Integer dapibus laoreet tincidunt. Vestibulum at erat eu dui convallis cursus. Sed sagittis ut nibh at feugiat. Duis vitae arcu eget risus mattis placerat. Donec eu metus a lorem sollicitudin sollicitudin id nec odio. Curabitur purus urna, vulputate at bibendum id, blandit ut arcu. Vestibulum enim ante, porta et aliquam id, pellentesque et augue.
`;

Press Enter to start editing.

Disabling Resize Transition

The textarea's height transition can be disabled by setting disableTransition.

import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { TextArea } from "@react-md/core/form/TextArea";
import { type ReactElement } from "react";

export default function DisablingResizeTransition(): ReactElement {
  return (
    <Form className={box({ stacked: true, align: "stretch" })}>
      <TextArea
        label="Label"
        placeholder="Placeholder..."
        disableTransition
        maxRows={8}
      />
    </Form>
  );
}

Press Enter to start editing.

Other Resize Behavior

The native resize behavior is also supported by setting the resize prop to one of: horizontal, vertical, both, or none.

import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { TextArea } from "@react-md/core/form/TextArea";
import { type ReactElement } from "react";

export default function OtherResizeBehavior(): ReactElement {
  return (
    <Form
      className={box({ stacked: true, align: "start", fullWidth: true })}
      style={{ minWidth: 0 }}
    >
      <TextArea
        label="Resize both"
        placeholder="Placeholder..."
        resize="both"
      />
      <TextArea
        label="Resize horizontal"
        placeholder="Placeholder..."
        resize="horizontal"
      />
      <TextArea
        label="Resize vertical"
        placeholder="Placeholder..."
        resize="vertical"
      />
      <TextArea label="No resize" placeholder="Placeholder..." resize="none" />
    </Form>
  );
}

Press Enter to start editing.

Text Field Addon

The TextField can render addons (normally icons) before and after the text of by using the leftAddon/rightAddon props. The addon will be placed above the <input> using absolute positioning and the <input> will gain additional padding-left/padding-right so the addons will not overlap with the text content.

Form Theme

"use client";

import { Avatar } from "@react-md/core/avatar/Avatar";
import { Box } from "@react-md/core/box/Box";
import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { Radio } from "@react-md/core/form/Radio";
import { TextField } from "@react-md/core/form/TextField";
import { type FormTheme } from "@react-md/core/form/types";
import { useRadioGroup } from "@react-md/core/form/useRadioGroup";
import { Typography } from "@react-md/core/typography/Typography";
import { typography } from "@react-md/core/typography/typographyStyles";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import { type ReactElement } from "react";

export default function TextFieldAddon(): ReactElement {
  const { value: theme, getRadioProps } = useRadioGroup<FormTheme>({
    name: "theme",
    defaultValue: "outline",
  });

  return (
    <Form className={box({ stacked: true, align: "stretch" })}>
      <TextField
        label="Label"
        theme={theme}
        placeholder="Placeholder"
        leftAddon={<FavoriteIcon />}
      />
      <TextField
        label="Label"
        theme={theme}
        placeholder="Placeholder"
        rightAddon={<FavoriteIcon />}
      />
      <TextField
        label="Label"
        theme={theme}
        placeholder="Placeholder"
        leftAddon={<FavoriteIcon />}
        rightAddon={<Avatar size="icon">A</Avatar>}
      />
      <Box stacked disablePadding align="start" fullWidth>
        <Typography>Form Theme</Typography>
        {themes.map((theme) => (
          <Radio
            key={theme}
            {...getRadioProps(theme)}
            label={theme}
            className={typography({ type: null, textTransform: "capitalize" })}
          />
        ))}
      </Box>
    </Form>
  );
}

const themes: readonly FormTheme[] = ["underline", "filled", "outline"];

Press Enter to start editing.

Non-icon Addons

If an addon is not icon sized, additional styling will be required. Here are a few available options:

  1. Modify the --rmd-text-field-padding-left/--rmd-text-field-padding-right to be the width of the addon plus some additional padding. This is the "best" option if the addons should still be rendered above the <input> element since it will also update the floating label position automatically
  2. Set disableLeftAddonStyles/disableRightAddonStyles to true to no longer use absolute positioning and render the addon inline
  3. Add custom styles with leftAddonProps/rightAddonProps as needed

Form Theme

"use client";

import { Avatar } from "@react-md/core/avatar/Avatar";
import { Box } from "@react-md/core/box/Box";
import { box } from "@react-md/core/box/styles";
import { Button } from "@react-md/core/button/Button";
import { Form } from "@react-md/core/form/Form";
import { Radio } from "@react-md/core/form/Radio";
import { TextField } from "@react-md/core/form/TextField";
import { type FormTheme } from "@react-md/core/form/types";
import { useRadioGroup } from "@react-md/core/form/useRadioGroup";
import { Typography } from "@react-md/core/typography/Typography";
import { typography } from "@react-md/core/typography/typographyStyles";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import { type ReactElement } from "react";

import styles from "./NonIconAddons.module.scss";

export default function NonIconAddons(): ReactElement {
  const { value: theme, getRadioProps } = useRadioGroup<FormTheme>({
    name: "theme",
    defaultValue: "outline",
  });

  return (
    <Form className={box({ stacked: true, align: "stretch" })}>
      <TextField
        label="Label"
        theme={theme}
        placeholder="Placeholder"
        className={styles.avatarField}
        leftAddon={<Avatar>B</Avatar>}
        rightAddon={<Avatar>A</Avatar>}
        defaultValue="Here's some long default text to show padding"
      />
      <TextField
        label="Label"
        theme={theme}
        placeholder="Placeholder"
        className={styles.buttonField}
        rightAddon={
          <Button aria-label="Favorite" buttonType="icon" iconSize="small">
            <FavoriteIcon />
          </Button>
        }
        // addons are normally presentational without pointer events, so set
        // `pointerEvents: true` to allow them for clickable elements
        rightAddonProps={{ pointerEvents: true }}
        defaultValue="Here's some long default text to show padding"
      />
      <TextField
        label="Label"
        theme={theme}
        placeholder="Placeholder"
        className={styles.buttonField}
        rightAddon={
          <Button aria-label="Favorite" buttonType="icon">
            <FavoriteIcon />
          </Button>
        }
        // addons are normally presentational without pointer events, so set
        // `pointerEvents: true` to allow them for clickable elements
        rightAddonProps={{ pointerEvents: true }}
        defaultValue="Here's some long default text to show padding"
      />
      <TextField
        label="Label"
        theme={theme}
        placeholder="Placeholder"
        rightAddon={
          <Button aria-label="Favorite" buttonType="icon">
            <FavoriteIcon />
          </Button>
        }
        disableRightAddonStyles
        defaultValue="Here's some long default text to show padding"
      />
      <Box stacked disablePadding align="start" fullWidth>
        <Typography>Form Theme</Typography>
        {themes.map((theme) => (
          <Radio
            key={theme}
            {...getRadioProps(theme)}
            label={theme}
            className={typography({ type: null, textTransform: "capitalize" })}
          />
        ))}
      </Box>
    </Form>
  );
}

const themes: readonly FormTheme[] = ["underline", "filled", "outline"];

Press Enter to start editing.

@use "everything";

.avatarField {
  @include everything.text-field-set-var(
    padding-left,
    calc(everything.avatar-get-var(size) + 1.5rem)
  );
  @include everything.text-field-set-var(
    padding-right,
    calc(everything.avatar-get-var(size) + 1rem)
  );
}

.buttonField {
  @include everything.text-field-set-var(padding-right, 4rem);
}

Press Enter to start editing.

Automatic Addon Padding

If the addons are dynamic or custom styles are not desired, the useTextFieldContainerAddons hook can be used to automatically update the --rmd-text-field-padding-left/--rmd-text-field-padding-right variables using the useResizeObserver behind the scenes.

The useTextFieldContainerAddons hook requires a leftAddon and rightAddon flag to be provided and will return a style object, leftAddonRef, and rightAddonRef which can be passed to the TextField component.

"use client";

import { Avatar } from "@react-md/core/avatar/Avatar";
import { box } from "@react-md/core/box/styles";
import { Button } from "@react-md/core/button/Button";
import { Chip } from "@react-md/core/chip/Chip";
import { cssUtils } from "@react-md/core/cssUtils";
import { Form } from "@react-md/core/form/Form";
import { TextField, type TextFieldProps } from "@react-md/core/form/TextField";
import { useTextFieldContainerAddons } from "@react-md/core/form/useTextFieldContainerAddons";
import { CircularProgress } from "@react-md/core/progress/CircularProgress";
import { useToggle } from "@react-md/core/useToggle";
import { loop } from "@react-md/core/utils/loop";
import { randomInt } from "@react-md/core/utils/randomInt";
import CancelIcon from "@react-md/material-icons/CancelIcon";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import { type ReactElement, useEffect, useState } from "react";

export default function AutomaticAddonPaddingExample(): ReactElement {
  const { leftAddon, rightAddon, toggle, running } = useRotatingAddons();
  const { style, leftAddonRef, rightAddonRef } = useTextFieldContainerAddons({
    leftAddon: !!leftAddon,
    rightAddon: !!rightAddon,

    // this will be merged with the returned `style`
    // style: {
    //   color: "red",
    // },

    // if there should be more of a gap between the addon and the input, use
    // these two options to add values to the `calc()` expression. This **must**
    // start with a `+ ` or `- `
    //
    // leftAddonExtraCalc: "+ var(--rmd-icon-spacing) - 0.125rem",
    // rightAddonExtraCalc: "+ calc(var(--rmd-icon-spacing) * 2)",

    // if refs are required for the addon container, they will be merged with
    // the returned leftAddonRef/rightAddonRef
    // leftAddonRef: customLeftAddonRef,
    // rightAddonRef: customRightAddonRef,
  });

  return (
    <Form
      style={{ maxWidth: "30rem" }}
      className={box({ align: "start", stacked: true, fullWidth: true })}
    >
      <TextField
        label="Label"
        placeholder="Placeholder"
        style={style}
        leftAddon={leftAddon}
        leftAddonProps={{ ref: leftAddonRef, pointerEvents: true }}
        rightAddon={rightAddon}
        rightAddonProps={{ ref: rightAddonRef, pointerEvents: true }}
        defaultValue="Here's some default content to show padding and overflow"
        inputClassName={cssUtils({ textOverflow: "ellipsis" })}
      />
      <Button onClick={toggle} themeType="outline" theme="primary">
        {running && <CircularProgress aria-label="Running" />}
        <span>{running ? "Stop" : "Start"}</span>
      </Button>
    </Form>
  );
}

const ADDONS = [
  <CircularProgress
    aria-label="Loading"
    dense
    disableCentered
    key="progress"
  />,
  <Chip key="chip">Chip</Chip>,
  <Button
    aria-label="Favorite"
    buttonType="icon"
    iconSize="small"
    key="favorite-button"
  >
    <FavoriteIcon />
  </Button>,
  <Avatar color="orange" key="orange-avatar">
    O
  </Avatar>,
  <Avatar color="red" key="red-avatar">
    R
  </Avatar>,
  <Chip key="cancelable-chip" rightAddon={<CancelIcon />} selected>
    Chip
  </Chip>,
];
const max = ADDONS.length - 1;

interface RotatingAddonsProps
  extends Pick<TextFieldProps, "leftAddon" | "rightAddon"> {
  toggle: () => void;
  running: boolean;
}

function useRotatingAddons(): RotatingAddonsProps {
  const [leftAddonIndex, setLeftAddonIndex] = useState<number | undefined>();
  const [rightAddonIndex, setRightAddonIndex] = useState<number | undefined>();
  const { toggle, toggled: running } = useToggle();

  useEffect(() => {
    if (!running) {
      return;
    }

    const interval = window.setInterval(() => {
      setLeftAddonIndex((prev) => {
        const next = randomInt({ min: 0, max });
        if (next === prev) {
          return loop({ value: prev, increment: true, max });
        }

        return next;
      });
      setRightAddonIndex((prev) => {
        const next = randomInt({ min: 0, max });
        if (next === prev) {
          return loop({ value: prev, increment: true, max });
        }

        return next;
      });
    }, 1500);

    return () => {
      window.clearInterval(interval);
    };
  }, [leftAddonIndex, rightAddonIndex, running]);

  return {
    toggle() {
      if (
        typeof leftAddonIndex === "undefined" &&
        typeof rightAddonIndex === "undefined"
      ) {
        setLeftAddonIndex(randomInt({ min: 0, max }));
        setRightAddonIndex(randomInt({ min: 0, max }));
      }

      toggle();
    },
    running,
    leftAddon: typeof leftAddonIndex === "number" && ADDONS[leftAddonIndex],
    rightAddon: typeof rightAddonIndex === "number" && ADDONS[rightAddonIndex],
  };
}

Press Enter to start editing.

Help Text and Error Text

The TextField component is wrapped in the FormMessageContainer so additional hint or error messages can be displayed.

This is some help text

This is some error text

import { box } from "@react-md/core/box/styles";
import { Form } from "@react-md/core/form/Form";
import { TextField } from "@react-md/core/form/TextField";
import { type ReactElement } from "react";

export default function HelpTextAndErrorText(): ReactElement {
  return (
    <Form className={box({ stacked: true, align: "start" })}>
      <TextField
        label="Label"
        placeholder="Placeholder"
        messageProps={{
          children: "This is some help text",
        }}
      />
      <TextField
        label="Label"
        error
        placeholder="Placeholder"
        messageProps={{
          error: true,
          children: "This is some error text",
        }}
      />
    </Form>
  );
}

Press Enter to start editing.

Text Field With Counter

A text field can also render an inline counter using the messageProps by providing the current length of the value and a maxLength.

Optional help text

0 / 30
"use client";

import { Form } from "@react-md/core/form/Form";
import { TextField } from "@react-md/core/form/TextField";
import { type ReactElement, useState } from "react";

const maxLength = 30;

export default function TextFieldWithCounter(): ReactElement {
  const [value, setValue] = useState("");
  const error = value.length > maxLength;
  return (
    <Form>
      <TextField
        label="Label"
        placeholder="Placeholder"
        value={value}
        onChange={(event) => {
          setValue(event.currentTarget.value);
        }}
        // Uncomment this line to allow the browser to prevent adding more
        // characters as well
        // maxLength={maxLength}
        messageProps={{
          error,
          length: value.length,
          maxLength,
          children: error ? "Value too long" : "Optional help text",
        }}
      />
    </Form>
  );
}

Press Enter to start editing.

Text Field Hook

The useTextField hook can be used to control the value of a single text field to conditionally display help text, error text, error icons, and inline counters. The hook also supports simple validation using the Constraint Validation API.

Check out the useTextField documentation for more information.

Alpha-numeric characters only

0 / 60
/* eslint-disable @typescript-eslint/no-unused-vars */
import { Box } from "@react-md/core/box/Box";
import { box } from "@react-md/core/box/styles";
import { Button } from "@react-md/core/button/Button";
import { Form } from "@react-md/core/form/Form";
import { TextField } from "@react-md/core/form/TextField";
import { useTextField } from "@react-md/core/form/useTextField";
import {
  defaultGetErrorIcon,
  defaultGetErrorMessage,
} from "@react-md/core/form/validation";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import { type ReactElement } from "react";

export default function TextFieldHookExample(): ReactElement {
  const { value, error, errorMessage, fieldRef, reset, setState, fieldProps } =
    useTextField({
      name: "example",
      counter: true,
      required: true,
      helpText: "Alpha-numeric characters only",
      pattern: "^[0-9A-Za-z]+$",
      minLength: 10,
      maxLength: 60,
      // This can be used to configure the error icon that gets placed as a
      // rightAddon
      getErrorIcon(options) {
        const { error, errorMessage, errorIcon } = options;
        if (!error) {
          return <FavoriteIcon />;
        }

        return defaultGetErrorIcon(options);
      },
      // This can be used to set a custom validation message when there
      // is an error in the input
      getErrorMessage(options) {
        const {
          isBlurEvent,
          isNumber,
          validationMessage,
          validationType,
          validity,
          value,
          maxLength,
          minLength,
          pattern,
          required,
        } = options;

        return defaultGetErrorMessage(options);
      },
    });
  return (
    <Form
      onReset={reset}
      className={box({
        stacked: true,
        align: "stretch",
        disablePadding: true,
        fullWidth: true,
      })}
    >
      <TextField label="Label" placeholder="Placeholder" {...fieldProps} />
      <Box disablePadding disableWrap justify="space-between">
        <Button type="reset" theme="warning" themeType="outline">
          Reset
        </Button>
        <Button type="submit" theme="primary" themeType="contained">
          Submit
        </Button>
      </Box>
    </Form>
  );
}

Press Enter to start editing.