Input

free

Input provides consistent text entry for forms, search, and editable notes.

Preview
Dark
import { PiMagnifyingGlass } from 'react-icons/pi'
import Input from '@/components/ui/Input';

export default function UsageDemo() {
  return (
    <div className="mx-auto w-full max-w-md">
      <Input
        aria-label="Search projects"
        placeholder="Search projects"
        prefix={<PiMagnifyingGlass className="text-base text-muted-foreground" />}
      />
    </div>
  );
}

Installation

Add this component with the NateUI CLI.

npx nateui@latest add Input

Examples

Basic

Preview
Dark
import Input from '@/components/ui/Input';

export default function BasicDemo() {
  return (
    <div className="mx-auto w-full max-w-md">
      <Input placeholder="Workspace name" />
    </div>
  );
}

Size

Preview
Dark
import Input from '@/components/ui/Input';

export default function SizeDemo() {
  return (
    <div className="mx-auto w-full max-w-md space-y-4">
      <Input size="sm" placeholder="Small input" />
      <Input placeholder="Medium input" />
      <Input size="lg" placeholder="Large input" />
    </div>
  );
}

Disabled

Preview
Dark
import Input from '@/components/ui/Input';

export default function DisabledDemo() {
  return (
    <div className="mx-auto grid w-full max-w-md gap-4 sm:grid-cols-2">
      <Input disabled defaultValue="Disabled value" />
      <Input readOnly defaultValue="Read only value" />
    </div>
  );
}

Affix

Preview
Dark
$USD
import { PiMagnifyingGlass, PiQuestion } from 'react-icons/pi'
import Input from '@/components/ui/Input';
import Tooltip from '@/components/ui/Tooltip';

export default function AffixDemo() {
  return (
    <div className="mx-auto w-full max-w-md space-y-4">
      <Input
        aria-label="Search customers"
        placeholder="Search customers"
        prefix={<PiMagnifyingGlass className="text-base text-muted-foreground" />}
      />

      <Input
        aria-label="Monthly budget"
        defaultValue="12000"
        prefix="$"
        suffix="USD"
      />

      <Input
        aria-label="Workspace slug"
        placeholder="northwind-sales"
        suffix={
          <Tooltip 
            title="Use lowercase letters, numbers, and dashes."
            tabIndex={0}
            aria-label="Workspace slug help"
          >
            <PiQuestion className="text-base text-muted-foreground cursor-help" />
          </Tooltip>
        }
      />
    </div>
  );
}

Password Visibility

Preview
Dark
import { useState } from 'react';
import { PiEye, PiEyeSlash } from 'react-icons/pi'
import Input from '@/components/ui/Input';

export default function PasswordVisibilityDemo() {
  const [visible, setVisible] = useState(false);

  return (
    <div className="mx-auto w-full max-w-md">
      <Input
        type={visible ? 'text' : 'password'}
        placeholder="Password"
        suffix={
          <button
            type="button"
            aria-label={visible ? 'Hide password' : 'Show password'}
            className="inline-flex cursor-pointer items-center text-muted-foreground transition-colors hover:text-foreground"
            onClick={() => setVisible((current) => !current)}
          >
            {visible ? (
              <PiEyeSlash className="text-base" />
            ) : (
              <PiEye className="text-base" />
            )}
          </button>
        }
      />
    </div>
  );
}

Textarea

Preview
Dark
import Input from '@/components/ui/Input';

export default function TextareaDemo() {
  return (
    <div className="mx-auto w-full max-w-md">
      <Input
        textArea
        rows={4}
        placeholder="Write an internal note"
        defaultValue="Follow up after the onboarding call."
      />
    </div>
  );
}

Invalid

Preview
Dark
Enter a valid email address.
import { useState } from 'react';
import { PiWarningCircle } from 'react-icons/pi'
import Button from '@/components/ui/Button';
import Form from '@/components/ui/Form';
import Input from '@/components/ui/Input';

export default function InvalidDemo() {
  const [invalid, setInvalid] = useState(true);

  return (
    <Form
      className="mx-auto w-full max-w-md"
      onSubmit={(event) => event.preventDefault()}
    >
      <Form.Field
        label="Invite email"
        htmlFor="invalid-email"
        invalid={invalid}
        errorMessage={invalid ? 'Enter a valid email address.' : undefined}
      >
        <Input
          id="invalid-email"
          name="email"
          type="email"
          invalid={invalid}
          defaultValue="alex@"
          suffix={invalid ? <PiWarningCircle className="text-base" /> : null}
        />
      </Form.Field>

      <div className="flex justify-end gap-2">
        <Button type="button" onClick={() => setInvalid((current) => !current)}>
          Set {invalid ? 'valid' : 'invalid'}
        </Button>
      </div>
    </Form>
  );
}

Controlled

Preview
Dark
Current value: northwind-sales
import { useState } from 'react';
import Input from '@/components/ui/Input';
import type { ChangeEvent } from 'react';

export default function ControlledDemo() {
  const [value, setValue] = useState('northwind-sales');

  const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
    setValue(event.target.value);
  };

  return (
    <div className="mx-auto w-full max-w-md space-y-2">
      <Input
        value={value}
        placeholder="Workspace slug"
        onChange={handleChange}
      />
      <div className="text-xs text-muted-foreground">
        Current value: {value || 'empty'}
      </div>
    </div>
  );
}

API

Input renders either a native input, a textarea, or an affixed input wrapped by InputWrapper.

Input

Input accepts the props below plus native input and textarea props, except the native size and prefix attributes.

PropDescriptionTypeDefault
classNameClass names applied to the input root, or to InputWrapper when prefix or suffix is present.string-
defaultValueInitial value for uncontrolled usage.string | number | readonly string[]-
disabledPrevents editing and applies disabled control styling.boolean-
invalidApplies invalid styling; also inherits invalid state from Form.Field.boolean-
onChangeCalled when the input value changes.InputChangeHandler-
placeholderPlaceholder text rendered by the native input or textarea.string-
prefixContent rendered before the editable value inside an affixed input.InputAffix-
readOnlyPrevents editing while keeping the field focusable.boolean-
refRef forwarded to the rendered input or textarea element.InputRef-
rowsNumber of visible text rows when textArea is true.number-
sizeControl size. Falls back to InputGroup size, then Form size, then ConfigProvider control size.ControlSizeConfigProvider controlSize
suffixContent rendered after the editable value inside an affixed input.InputAffix-
textAreaRenders a textarea instead of an input.booleanfalse
typeNative input type when textArea is false.HTMLInputTypeAttribute'text'
valueControlled value. undefined and null are normalized to an empty string.string | number | readonly string[]-
...nativeInputPropsNative props for the rendered input or textarea, except size and prefix.NativeInputProps-

InputWrapper

InputWrapper is the affix container used by Input when prefix or suffix is provided.

PropDescriptionTypeDefault
childrenEditable control rendered between the affix slots.ReactNode-
classNameClass names applied to the wrapper root.string-
disabledApplies disabled wrapper and affix styling.boolean-
invalidApplies invalid wrapper and affix styling; also inherits invalid state from Form.Field.boolean-
prefixContent rendered before children.ReactNode-
readOnlyRemoves focus ring styling when the wrapped control is read-only.boolean-
refRef forwarded to the wrapper span.Ref<HTMLSpanElement>-
sizeControl size. Falls back to InputGroup size, then Form size, then ConfigProvider control size.ControlSizeConfigProvider controlSize
suffixContent rendered after children.ReactNode-
...nativeSpanPropsNative props applied to the wrapper span, except prefix.NativeInputWrapperProps-

Types

type ControlSize = 'sm' | 'md' | 'lg';
type InputAffix = string | ReactNode;
type InputChangeHandler = ChangeEventHandler<
  HTMLInputElement | HTMLTextAreaElement
>;
type InputRef = Ref<ElementType | HTMLInputElement | HTMLTextAreaElement>;
type NativeInputProps = Omit<
  InputHTMLAttributes<HTMLInputElement | HTMLTextAreaElement>,
  'size' | 'prefix'
>;
type NativeInputWrapperProps = Omit<ComponentProps<'span'>, 'prefix'>;