Skip to main content

Input

A versatile Input component that provides form-friendly input fields with integrated labels, error handling, and optional left/right sections. Built with accessibility and customization in mind.


Props

NameTypeDefaultDescription
labelstringLabel text displayed above the input.
errors`string[]stringReactNode`
requiredbooleanfalseShows a required indicator (asterisk) next to the label.
leftSectionReactNodeContent displayed on the left side inside the input.
rightSectionReactNodeContent displayed on the right side inside the input.
labelPropsComponentPropsWithoutRef<"label">Props passed to the label element.
labelPosition`"x""y"`"y"
...restComponentPropsWithoutRef<"input">Any native <input> props are forwarded.

Note The component automatically applies number pattern validation for type="number" inputs.


Usage

Basic Input

The most basic usage includes a label and placeholder.

<Input
label="Full Name"
placeholder="Enter your name"
required
/>

Preview

Input with Errors

Pass an array of strings to the errors prop to display validation messages. The component will automatically apply error styling.

<Input
label="Email"
placeholder="you@example.com"
defaultValue="not-an-email"
errors={["Please enter a valid email"]}
/>

Preview

Please enter a valid email

Input with Sections (Prefix/Suffix)

Use leftSection and rightSection to add prefixes, suffixes, icons, or buttons inside the input.

<Input
label="Amount"
placeholder="0.00"
type="number"
leftSection={<span style={{ padding: "0 8px", opacity: 0.7 }}>$</span>}
rightSection={<span style={{ padding: "0 8px", opacity: 0.7 }}>USD</span>}
/>

Preview

$USD

Password Input with Show/Hide

You can use the rightSection to create a toggle for password visibility.

function PasswordInput() {
const [visible, setVisible] = React.useState(false);
return (
<Input
label="Password"
placeholder="Enter password"
type={visible ? "text" : "password"}
rightSection={
<button
type="button"
onClick={() => setVisible((v) => !v)}
style={{ border: "none", background: "transparent", cursor: "pointer" }}
aria-label={visible ? "Hide password" : "Show password"}
>
{visible ? "🙈" : "👁️"}
</button>
}
required
/>
);
}

Preview

Horizontal Label Layout

Set labelPosition="x" to align the label and input horizontally. You may need to provide a minWidth to the label via labelProps for alignment.

<Input
label="Username"
placeholder="john_doe"
labelPosition="x"
labelProps={{ style: { minWidth: "100px" } }}
/>

Preview

Disabled Input

Pass the native disabled prop to disable interaction and apply disabled styles.

<Input
label="Disabled Field"
placeholder="Cannot edit"
disabled
defaultValue="Some read-only value"
/>

Preview


Accessibility

  • Semantics: Renders a proper <label> element linked to the input via htmlFor.
  • Error states: Errors are announced to screen readers via the data-error attribute.
  • Required fields: Visual indicator (asterisk) and semantic required attribute.
  • Keyboard navigation: Full keyboard support inherited from native input.

Tip Always provide descriptive labels and error messages for screen reader users.


Styles & customization

Runtime classes

  • kui-input-container: The main wrapper for the label, input, and error message.
  • kui-input-inner: The direct wrapper around the input field and its sections.
  • kui-input: The <input> element itself.
  • kui-input-label: The <label> element.
  • kui-input-error-message: The <span> that displays the error message.

Tokens used by the default styles

  • Colors: --kui-neutral-*, --kui-primary-*, --kui-danger-*
  • Spacing: --kui-spacing-2xs, --kui-spacing-xs, --kui-spacing-sm
  • Typography: --kui-text-base, --kui-text-sm
  • Rounding: --kui-rounded

Types (reference)

import { ComponentPropsWithoutRef, ReactNode } from "react";

type LabelPositionBase = "x" | "y";

export type InputProps = ComponentPropsWithoutRef<"input"> & {
label?: string;
labelProps?: ComponentPropsWithoutRef<"label">;
errors?: string[] | string | ReactNode;
required?: boolean;
onMaxExited?: VoidFunction;
leftSection?: ReactNode;
rightSection?: ReactNode;
labelPosition?: LabelPositionBase;
};