Input

A form input component for text entry. Supports all native input types.

Installation

Terminal
ReactTailwind
TSX
npx @uiblox/cli add input

Visual Variations

Toggle between three different visual styles: default (bordered), filled (background), and underline (minimal).

Standard text input

Input with search icon

Non-editable state

Basic Usage

Copy & paste ready
import { Input } from "@/components/ui";

<Input placeholder="Enter text..." />

Input Types

Copy & paste ready
<Input type="text" placeholder="Text input" />
<Input type="email" placeholder="Email input" />
<Input type="password" placeholder="Password input" />
<Input type="number" placeholder="Number input" />
<Input type="search" placeholder="Search..." />

Disabled State

Copy & paste ready
<Input disabled placeholder="Disabled input" />
<Input disabled value="Disabled with value" />

With Label

Copy & paste ready
<div className="space-y-2">
  <label htmlFor="email" className="text-sm font-medium text-gray-700">
    Email
  </label>
  <Input id="email" type="email" placeholder="you@example.com" />
</div>

With Button

Copy & paste ready
<div className="flex gap-2">
  <Input placeholder="Search..." className="flex-1" />
  <Button>Search</Button>
</div>

File Input

Copy & paste ready
<Input type="file" />

Accessibility

Form inputs are critical for accessibility. Every input must be properly labeled and error states must be clearly communicated.

Keyboard Support

  • Tab - Move focus to input
  • Shift+Tab - Move focus away
  • Standard text editing keys supported

Required Attributes

  • Always include id and matching <label>
  • Use aria-describedby for helper text
  • Use aria-invalid for error states

Accessible Input Patterns

// Always use labels - never rely on placeholder alone
<div className="space-y-2">
  <label htmlFor="email" className="text-sm font-medium text-white">
    Email address
    <span className="text-red-400" aria-hidden="true">*</span>
    <span className="sr-only">(required)</span>
  </label>
  <Input
    id="email"
    type="email"
    required
    aria-required="true"
    placeholder="you@example.com"
    aria-describedby="email-hint"
  />
  <p id="email-hint" className="text-sm text-[#94a3b8]">
    We'll never share your email with anyone.
  </p>
</div>

// Error state with proper announcements
<div className="space-y-2">
  <label htmlFor="password" className="text-sm font-medium text-white">
    Password
  </label>
  <Input
    id="password"
    type="password"
    aria-invalid="true"
    aria-describedby="password-error"
    className="border-red-500"
  />
  <p id="password-error" className="text-sm text-red-400" role="alert">
    Password must be at least 8 characters
  </p>
</div>

// Search input with role
<div className="relative" role="search">
  <label htmlFor="search" className="sr-only">
    Search products
  </label>
  <Input
    id="search"
    type="search"
    placeholder="Search products..."
    aria-label="Search products"
  />
</div>

// Disabled input with explanation
<div className="space-y-2">
  <label htmlFor="readonly" className="text-sm font-medium text-[#94a3b8]">
    Account ID(read-only)
  </label>
  <Input
    id="readonly"
    value="ACC-12345"
    disabled
    aria-disabled="true"
    aria-describedby="readonly-hint"
  />
  <p id="readonly-hint" className="text-sm text-[#64748b]">
    This field cannot be edited
  </p>
</div>

Best Practices

  • Never use placeholder as the only label
  • • Associate error messages with aria-describedby
  • • Use role="alert" for dynamic error messages
  • • Mark required fields with both visual indicator and aria-required
  • • Provide clear instructions for expected input format

Props

Input accepts all standard HTML input attributes plus:

PropTypeDefaultDescription
typestring"text"The input type (text, email, password, number, etc.)
placeholderstring-Placeholder text
disabledbooleanfalseWhether the input is disabled
classNamestring-Additional CSS classes

Source Code

Copy this code into src/components/ui/input.tsx:

input.tsx
ReactTailwind
TSX
import * as React from "react";
import { cn } from "@/lib/utils";

export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}

const Input = React.forwardRef<HTMLInputElement, InputProps>(
  ({ className, type, ...props }, ref) => {
    return (
      <input
        type={type}
        className={cn(
          "flex h-10 w-full rounded-md border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-3 py-2 text-sm text-gray-900 dark:text-gray-100 ring-offset-background",
          "file:border-0 file:bg-transparent file:text-sm file:font-medium",
          "placeholder:text-gray-500 dark:placeholder:text-gray-400",
          "focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2",
          "disabled:cursor-not-allowed disabled:opacity-50",
          className
        )}
        ref={ref}
        {...props}
      />
    );
  }
);
Input.displayName = "Input";

export { Input };