---
layout: ~/layouts/MdxDocLayout.astro
title: "Input"
description: "A text input field for user input with built-in label, description, and error support."
sourceFile: "components/input"
baseUIComponent: "input"
---

import ComponentExample from "~/components/docs/ComponentExample.astro";
import ComponentSection from "~/components/docs/ComponentSection.astro";
import CodeBlock from "~/components/docs/CodeBlock.astro";
import Heading from "~/components/docs/Heading.astro";
import PropsTable from "~/components/docs/PropsTable.astro";
import {
  InputBasicDemo,
  InputWithLabelDemo,
  InputErrorStringDemo,
  InputErrorObjectDemo,
  InputSizesDemo,
  InputDisabledDemo,
  InputBareDemo,
  InputTypesDemo,
  InputOptionalFieldDemo,
  InputLabelTooltipDemo,
  InputReactNodeLabelDemo,
} from "~/components/demos/InputDemo";

{/* Hero Demo */}

<ComponentSection>
  <ComponentExample demo="InputBasicDemo">
    <InputBasicDemo client:visible />
  </ComponentExample>
</ComponentSection>

{/* Installation */}

<ComponentSection>
  <Heading level={2}>Installation</Heading>
  <Heading level={3}>Barrel</Heading>
  <CodeBlock code={`import { Input } from "@cloudflare/kumo";`} lang="tsx" />
  <Heading level={3}>Granular</Heading>
  <CodeBlock
    code={`import { Input } from "@cloudflare/kumo/components/input";`}
    lang="tsx"
  />
</ComponentSection>

{/* Usage */}

<ComponentSection>
  <Heading level={2}>Usage</Heading>
  <Heading level={3}>With Built-in Field (Recommended)</Heading>
  <p>
    Use the `label` prop to enable the built-in Field wrapper with label, description, and error support.
  </p>

```tsx
import { Input } from "@cloudflare/kumo";

export default function Example() {
  return (
    <Input
      label="Email"
      placeholder="you@example.com"
      description="We'll never share your email"
    />
  );
}
```

<Heading level={3}>Bare Input (Custom Layouts)</Heading>
<p>
  For custom form layouts, use Input without `label`. Must provide `aria-label`
  or `aria-labelledby` for accessibility.
</p>

```tsx
import { Input } from "@cloudflare/kumo";

export default function Example() {
  return <Input placeholder="Search..." aria-label="Search products" />;
}
```

</ComponentSection>

{/* Examples */}

<ComponentSection>
  <Heading level={2}>Examples</Heading>

<Heading level={3}>With Label and Description</Heading>
<p>
  The `label` prop enables the built-in Field wrapper with automatic vertical
  layout (label above input).
</p>
<ComponentExample demo="InputWithLabelDemo">
  <InputWithLabelDemo client:visible />
</ComponentExample>

<Heading level={3}>With Error (String)</Heading>
<p>
  Pass `error` as a string for simple error messages. Error styling is
  automatically applied when the `error` prop is truthy.
</p>
<ComponentExample demo="InputErrorStringDemo">
  <InputErrorStringDemo client:visible />
</ComponentExample>

<Heading level={3}>With Error (Validation Object)</Heading>
<p>
  Pass `error` as an object with `message` and `match` for HTML5 validation.
  Error shows when field validity matches.
</p>
<ComponentExample demo="InputErrorObjectDemo">
  <InputErrorObjectDemo client:visible />
</ComponentExample>

<Heading level={3}>Input Sizes</Heading>
<p>Four sizes available: `xs`, `sm`, `base` (default), `lg`.</p>
<ComponentExample demo="InputSizesDemo">
  <InputSizesDemo client:visible />
</ComponentExample>

<Heading level={3}>Disabled</Heading>
<ComponentExample demo="InputDisabledDemo">
  <InputDisabledDemo client:visible />
</ComponentExample>

<Heading level={3}>Optional Field</Heading>
<p>Set `required={false}` to show "(optional)" text after the label.</p>
<ComponentExample demo="InputOptionalFieldDemo">
  <InputOptionalFieldDemo client:visible />
</ComponentExample>

<Heading level={3}>With Label Tooltip</Heading>
<p>Use `labelTooltip` to add an info icon with additional context on hover.</p>
<ComponentExample demo="InputLabelTooltipDemo">
  <InputLabelTooltipDemo client:visible />
</ComponentExample>

<Heading level={3}>ReactNode Label</Heading>
<p>The `label` prop accepts ReactNode for rich formatting.</p>
<ComponentExample demo="InputReactNodeLabelDemo">
  <InputReactNodeLabelDemo client:visible />
</ComponentExample>

<Heading level={3}>Bare Input (No Label)</Heading>
<p>
  Input without `label` renders as a bare input. Must provide `aria-label` for
  accessibility.
</p>
<ComponentExample demo="InputBareDemo">
  <InputBareDemo client:visible />
</ComponentExample>

  <Heading level={3}>Input Types</Heading>
  <p>
    Supports all HTML input types: `text`, `email`, `password`, `number`, `tel`, `url`, etc.
  </p>
  <ComponentExample demo="InputTypesDemo">
    <InputTypesDemo client:visible />
  </ComponentExample>
</ComponentSection>

{/* API Reference */}

<ComponentSection>
  <Heading level={2}>API Reference</Heading>
  <p>
    Input accepts all standard HTML input attributes plus the following:
  </p>
  <PropsTable component="Input" />

  <Heading level={3}>Validation Error Types</Heading>
  <p>
    When using `error` as an object, the `match` property corresponds to HTML5 ValidityState values:
  </p>
  <div class="overflow-x-auto">
    <table class="w-full text-sm">
      <thead>
        <tr class="border-b border-kumo-hairline">
          <th class="px-4 py-3 text-left font-semibold">Match</th>
          <th class="px-4 py-3 text-left font-semibold">Description</th>
        </tr>
      </thead>
      <tbody class="text-kumo-strong">
        <tr class="border-b border-kumo-hairline">
          <td class="px-4 py-3 font-mono text-xs">valueMissing</td>
          <td class="px-4 py-3 text-xs">Required field is empty</td>
        </tr>
        <tr class="border-b border-kumo-hairline">
          <td class="px-4 py-3 font-mono text-xs">typeMismatch</td>
          <td class="px-4 py-3 text-xs">Value doesn't match type (e.g., invalid email)</td>
        </tr>
        <tr class="border-b border-kumo-hairline">
          <td class="px-4 py-3 font-mono text-xs">patternMismatch</td>
          <td class="px-4 py-3 text-xs">Value doesn't match pattern attribute</td>
        </tr>
        <tr class="border-b border-kumo-hairline">
          <td class="px-4 py-3 font-mono text-xs">tooShort</td>
          <td class="px-4 py-3 text-xs">Value shorter than minLength</td>
        </tr>
        <tr class="border-b border-kumo-hairline">
          <td class="px-4 py-3 font-mono text-xs">tooLong</td>
          <td class="px-4 py-3 text-xs">Value longer than maxLength</td>
        </tr>
        <tr class="border-b border-kumo-hairline">
          <td class="px-4 py-3 font-mono text-xs">rangeUnderflow</td>
          <td class="px-4 py-3 text-xs">Value less than min</td>
        </tr>
        <tr class="border-b border-kumo-hairline">
          <td class="px-4 py-3 font-mono text-xs">rangeOverflow</td>
          <td class="px-4 py-3 text-xs">Value greater than max</td>
        </tr>
        <tr class="border-b border-kumo-hairline">
          <td class="px-4 py-3 font-mono text-xs">true</td>
          <td class="px-4 py-3 text-xs">Always show error (for server-side validation)</td>
        </tr>
      </tbody>
    </table>
  </div>
</ComponentSection>

{/* Accessibility */}

<ComponentSection>
  <Heading level={2}>Accessibility</Heading>
  <div class="space-y-4 text-sm">
    <div>
      <h3 class="mb-2 font-semibold">Label Requirement</h3>
      <p class="text-kumo-strong">
        Inputs require an accessible name via one of:
      </p>
      <ul class="mt-2 ml-4 list-disc space-y-1 text-kumo-strong">
        <li>`label` prop (recommended)</li>
        <li>`placeholder` + `aria-label` for bare inputs</li>
        <li>`aria-labelledby` for custom label association</li>
      </ul>
      <p class="mt-2 text-kumo-strong">
        Missing accessible names trigger console warnings in development.
      </p>
    </div>
    <div>
      <h3 class="mb-2 font-semibold">Error Association</h3>
      <p class="text-kumo-strong">
        Error messages are automatically associated with the input via ARIA
        attributes for screen reader announcement.
      </p>
    </div>
  </div>
</ComponentSection>
