Gecko UIGecko UI

RHFInput

Text input component integrated with React Hook Form

RHFInput

A text input component integrated with React Hook Form that automatically displays error states with a red border when validation fails. Supports value transformation for input/output formatting.

Installation

import { RHFInput } from '@geckoui/geckoui';

Basic Usage

import { useForm, FormProvider } from 'react-hook-form';
import { RHFInput } from '@geckoui/geckoui';

function Example() {
  const methods = useForm({
    defaultValues: {
      username: ''
    }
  });

  return (
    <FormProvider {...methods}>
      <RHFInput name="username" placeholder="Enter username" />
    </FormProvider>
  );
}

Props API

Extends all props from Input component plus:

PropTypeDefaultDescription
namestring-Field name (required)
controlControlAuto-injectedOptional: Pass explicitly for nested forms or custom form context
rulesRegisterOptions-Inline validation rules (see Inline Rules Validation)
transform{ input, output }-Value transformation functions
prefix`ReactNode \Function`-
suffix`ReactNode \Function`-
onChange(value: string) => void-Change callback (receives value, not event)
onBlur(value: string) => void-Blur callback
...restInputProps-All Input component props

Examples

With Validation (Zod)

import { useForm, FormProvider } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { RHFInput, RHFInputGroup, Button } from '@geckoui/geckoui';

const schema = z.object({
  email: z.string().email('Invalid email address').min(1, 'Email is required')
});

function Example() {
  const methods = useForm({
    resolver: zodResolver(schema),
    mode: 'onBlur', // Validate on blur
    defaultValues: { email: '' }
  });

  return (
    <FormProvider {...methods}>
      <RHFInputGroup label="Email Address" required>
        <RHFInput name="email" type="email" placeholder="Enter email" />
      </RHFInputGroup>
    </FormProvider>
  );
}

Inline Rules Validation

For simple forms, you can use the rules prop for inline validation instead of a schema resolver:

<RHFInput
  name="email"
  rules={{
    required: 'Email is required',
    pattern: {
      value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
      message: 'Invalid email address'
    }
  }}
/>

<RHFInput
  name="username"
  rules={{
    required: 'Username is required',
    minLength: { value: 3, message: 'Must be at least 3 characters' },
    maxLength: { value: 20, message: 'Must be 20 characters or less' }
  }}
/>

Note: For complex forms with many fields, we recommend using a schema resolver (Zod, Yup) instead of inline rules for better maintainability and type safety.

With Prefix and Suffix

<RHFInput
  name="amount"
  type="number"
  prefix="$"
  suffix="USD"
  placeholder="0.00"
/>

With Value Transformation

Transform values on input/output (e.g., phone number formatting):

const formatPhone = (value: string) => {
  const cleaned = value.replace(/\\D/g, '');
  const match = cleaned.match(/^(\\d{3})(\\d{3})(\\d{4})$/);
  if (match) {
    return `(${match[1]}) ${match[2]}-${match[3]}`;
  }
  return value;
};

const sanitizePhone = (value: string) => {
  return value.replace(/\\D/g, '');
};

<RHFInput
  name="phone"
  placeholder="Phone number"
  transform={{
    input: formatPhone,
    output: sanitizePhone
  }}
/>

Custom Suffix with Error Icon

<RHFInput
  name="password"
  type="password"
  suffix={({ fieldState }) => <>fieldState.error ? <ErrorIcon /> : null</>}
/>

Disabled State

<RHFInput name="readonly" disabled />

Complete Form Example

import { useForm, FormProvider } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { RHFInput, RHFInputGroup, Button } from '@geckoui/geckoui';

const schema = z.object({
  username: z.string().min(3, 'Username must be at least 3 characters'),
  email: z.string().email('Invalid email address'),
  password: z.string().min(8, 'Password must be at least 8 characters')
});

function RegistrationForm() {
  const methods = useForm({
    resolver: zodResolver(schema),
    mode: 'onBlur', // Validate on blur
    defaultValues: {
      username: '',
      email: '',
      password: ''
    }
  });

  return (
    <FormProvider {...methods}>
      <form onSubmit={methods.handleSubmit(console.log)}>
        <RHFInputGroup label="Username" required>
          <RHFInput name="username" placeholder="Enter username" />
        </RHFInputGroup>

        <RHFInputGroup label="Email Address" required>
          <RHFInput name="email" type="email" placeholder="Enter email" />
        </RHFInputGroup>

        <RHFInputGroup label="Password" required>
          <RHFInput name="password" type="password" placeholder="Enter password" />
        </RHFInputGroup>

        <Button type="submit">Submit</Button>
      </form>
    </FormProvider>
  );
}

Error States

RHFInput automatically displays error states with:

  • Red border via GeckoUIRHFInput--error CSS class when fieldState.error exists
  • No automatic error icon (you must add via suffix prop if desired)

Use RHFInputGroup component for label + input + error layout:

<RHFInputGroup label="Username" required>
  <RHFInput name="username" />
</RHFInputGroup>

Or combine with RHFError component manually:

<RHFInput name="username" />
<RHFError name="username" />

Tip: Use mode: 'onBlur' in useForm to validate on blur for better UX:

const methods = useForm({
  resolver: zodResolver(schema),
  mode: 'onBlur' // Validates when user leaves the field
});

Value Transformation

The transform prop allows formatting values differently for display vs storage:

// Currency formatting
<RHFInput
  name="price"
  transform={{
    input: (value) => value ? `$${value}` : '',
    output: (value) => value.replace('$', '')
  }}
/>

// Uppercase transformation
<RHFInput
  name="code"
  transform={{
    input: (value) => value?.toUpperCase() || '',
    output: (value) => value
  }}
/>

Dynamic Prefix/Suffix

Both prefix and suffix can be functions that receive render props:

<RHFInput
  name="email"
  prefix={({ field }) => (
    field.value ? <CheckIcon /> : <MailIcon />
  )}
  suffix={({ fieldState }) => (
    fieldState.error ? <ErrorIcon /> : null
  )}
/>

Accessibility

  • Inherits all accessibility features from Input component
  • Proper ARIA attributes for error states
  • Keyboard navigable
  • Screen reader compatible