Skip to main content

Overview

The TextField component is a fully-featured input field with support for labels, error states, helper text, and left/right icons. Built with TypeScript and React forwardRef for maximum flexibility and accessibility. Source: src/shared/ui/TextField/TextField.tsx

Basic Usage

import { TextField } from '@/shared/ui/TextField/TextField';

function Example() {
  return (
    <TextField
      label="Email"
      placeholder="Enter your email"
      type="email"
    />
  );
}

Props

The TextField component extends all standard HTML input attributes and includes these additional props:
label
string
Label text displayed above the input field.Provides context and improves accessibility by describing the input’s purpose.
error
boolean
default:"false"
When true, applies error styling to the input field and displays the helper text in error state.Use this to indicate validation errors or other input problems.
helperText
string
Helper or error message displayed below the input field.Typically used to provide guidance or display validation errors when error is true.
LeftIcon
LucideIcon
A Lucide React icon component to display on the left side of the input.Useful for search fields, username inputs, or other contextual indicators.
RightIcon
LucideIcon
A Lucide React icon component to display on the right side of the input.Commonly used for password visibility toggles, clear buttons, or validation indicators.
className
string
Additional CSS class names for custom styling.
style
React.CSSProperties
Inline styles to apply to the text field container.

Basic Input

Simple input field without additional features:
<TextField placeholder="Enter text" />

With Label

Add a label for better accessibility and user guidance:
<TextField
  label="Username"
  placeholder="Enter your username"
/>

With Icons

Left Icon

Display an icon on the left side of the input:
import { TextField } from '@/shared/ui/TextField/TextField';
import { Search } from 'lucide-react';

<TextField
  label="Search"
  placeholder="Search items..."
  LeftIcon={Search}
/>

Right Icon

Display an icon on the right side of the input:
import { TextField } from '@/shared/ui/TextField/TextField';
import { Mail } from 'lucide-react';

<TextField
  label="Email"
  placeholder="[email protected]"
  type="email"
  RightIcon={Mail}
/>
The TextField component uses Lucide React for icons. You can use any Lucide icon by importing it from the lucide-react package.

Error State

Display validation errors with error styling and helper text:
import { TextField } from '@/shared/ui/TextField/TextField';
import { AlertCircle } from 'lucide-react';

<TextField
  label="Email"
  placeholder="[email protected]"
  error={true}
  helperText="Please enter a valid email address"
  RightIcon={AlertCircle}
/>

Real-World Examples

Search Field

import { TextField } from '@/shared/ui/TextField/TextField';
import { Search } from 'lucide-react';
import { useState } from 'react';

function SearchBar() {
  const [query, setQuery] = useState('');
  
  return (
    <TextField
      label="Search Auctions"
      placeholder="Search by title, category, or item..."
      value={query}
      onChange={(e) => setQuery(e.target.value)}
      LeftIcon={Search}
    />
  );
}

Login Form

import { TextField } from '@/shared/ui/TextField/TextField';
import { Mail, Lock } from 'lucide-react';
import { Button } from '@/shared/ui/button/Button';

function LoginForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [errors, setErrors] = useState({});
  
  return (
    <form>
      <TextField
        label="Email"
        type="email"
        placeholder="[email protected]"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        LeftIcon={Mail}
        error={!!errors.email}
        helperText={errors.email}
      />
      
      <TextField
        label="Password"
        type="password"
        placeholder="Enter your password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        LeftIcon={Lock}
        error={!!errors.password}
        helperText={errors.password}
      />
      
      <Button type="submit" variant="primary" block>
        Sign In
      </Button>
    </form>
  );
}

Validated Input with Real-time Feedback

import { TextField } from '@/shared/ui/TextField/TextField';
import { Check, X } from 'lucide-react';
import { useState, useEffect } from 'react';

function UsernameInput() {
  const [username, setUsername] = useState('');
  const [isValid, setIsValid] = useState(false);
  const [error, setError] = useState('');
  
  useEffect(() => {
    if (username.length < 3) {
      setIsValid(false);
      setError('Username must be at least 3 characters');
    } else if (!/^[a-zA-Z0-9_]+$/.test(username)) {
      setIsValid(false);
      setError('Username can only contain letters, numbers, and underscores');
    } else {
      setIsValid(true);
      setError('');
    }
  }, [username]);
  
  return (
    <TextField
      label="Username"
      placeholder="Choose a username"
      value={username}
      onChange={(e) => setUsername(e.target.value)}
      error={!!error && username.length > 0}
      helperText={error}
      RightIcon={username.length > 0 ? (isValid ? Check : X) : undefined}
    />
  );
}

Bid Amount Input

import { TextField } from '@/shared/ui/TextField/TextField';
import { DollarSign } from 'lucide-react';
import { useState } from 'react';

function BidInput({ minBid }: { minBid: number }) {
  const [amount, setAmount] = useState('');
  const [error, setError] = useState('');
  
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value;
    setAmount(value);
    
    const numValue = parseFloat(value);
    if (isNaN(numValue)) {
      setError('Please enter a valid number');
    } else if (numValue < minBid) {
      setError(`Minimum bid is $${minBid}`);
    } else {
      setError('');
    }
  };
  
  return (
    <TextField
      label="Bid Amount"
      type="number"
      placeholder={`Min: $${minBid}`}
      value={amount}
      onChange={handleChange}
      LeftIcon={DollarSign}
      error={!!error}
      helperText={error || `Minimum bid: $${minBid}`}
    />
  );
}

TypeScript Types

import type { LucideIcon } from "lucide-react";
import type React from "react";

interface TextFieldProps extends React.InputHTMLAttributes<HTMLInputElement> {
  label?: string;
  error?: boolean;
  helperText?: string;
  LeftIcon?: LucideIcon;
  RightIcon?: LucideIcon;
  className?: string;
  style?: React.CSSProperties;
}

Accessibility

  • Uses semantic HTML with <label> and <input> elements
  • Labels are properly associated with inputs via htmlFor
  • Error states are indicated with aria-invalid and aria-describedby
  • Forwards refs for focus management and advanced accessibility features
  • Helper text provides additional context for screen readers
Always provide a label prop for better accessibility, even if you hide it visually. Screen readers need descriptive labels to understand input purpose.

Best Practices

Labels improve accessibility and help users understand what information is expected. Even if you want a minimal design, provide a label for screen readers.
Set the correct type attribute (email, password, tel, number, etc.) to provide better mobile keyboard experiences and browser validation.
Error messages should be specific and actionable. Instead of “Invalid input”, use “Email must contain an @ symbol” or “Password must be at least 8 characters”.
Choose icons that clearly represent the input’s purpose. Common patterns:
  • Search: Search icon (left)
  • Email: Mail icon (left or right)
  • Password: Lock icon (left), Eye icon for visibility toggle (right)
  • Username: User icon (left)
  • Validation: Check or X icon (right)

Known Limitations

The current implementation has hardcoded placeholder text and IDs in some instances (see TextField.tsx:30-32). These are overridden by props, but you may want to refine the component for production use.

Button

Commonly used with TextField in forms

Typography

For consistent label and helper text styling

Build docs developers (and LLMs) love