Gecko UIGecko UI

RHFInput

Text input component integrated with React Hook Form

You are reading the v1 docs. v2 renamed several props and replaced GeckoUIPortal with GeckoUIProvider. Read this page in v2.

Live examples are only rendered in the v2 docs.

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 data-error attribute on the root element when fieldState.error exists
  • No automatic error icon (you must add via suffix prop if desired)
.GeckoUIRHFInput[data-error] {
  border-color: red;
}

Recommended: Use RHFInputGroup for label + input + error layouts. It automatically detects the RHF input and displays validation errors:

<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
  • Input - Base input component
  • RHFTextarea - Multi-line text input
  • RHFNumberInput - Number-only input
  • RHFError - Error message display