본문으로 건너뛰기
Amineslab UI

Component

FormField

기본 입력 하나에 라벨·필수 표시·설명·오류를 연결합니다.

Playground

옵션을 조절하며 화면과 사용 코드를 함께 확인하세요.

PC 1280px
Controls

오류 메시지 표시

조합할 기본 입력

Code
import { FormField, NumberInput, Textarea, Input } from '@amineslab/ui'

function formFieldPlaceholder(control: string) {
    return control === 'NumberInput' ? '금액을 입력해 주세요' : '내용을 입력해 주세요';
}

function FormFieldExample({ label = '프로젝트 이름', description = '팀에서 식별할 이름입니다.', required = false, error = false, disabled = false, control = 'Input', }: {
    label?: string;
    description?: string;
    required?: boolean;
    error?: boolean;
    disabled?: boolean;
    control?: string;
}) {
    return (<FormField label={label} description={description} required={required} disabled={disabled} error={error ? '입력 내용을 확인해 주세요.' : undefined}>
      {control === 'NumberInput' ? (<NumberInput placeholder={formFieldPlaceholder(control)}/>) : control === 'Textarea' ? (<Textarea placeholder={formFieldPlaceholder(control)}/>) : (<Input placeholder={formFieldPlaceholder(control)}/>)}
    </FormField>);
}

export default function ExamplePreview() {
  return (<FormFieldExample {...({"label":"프로젝트 이름","description":"팀에서 식별할 이름입니다.","required":false,"error":false,"disabled":false,"control":"Input"} as const)}/>)
}

Props

@amineslab/ui에서 FormField를 가져옵니다.

import { FormField } from '@amineslab/ui'

FormField

기본 입력을 위한 단일 필드 구성입니다.

NameTypeDefaultDescription
children*ReactElement-Input·NumberInput 등 기본 입력 하나입니다.
label*ReactNode-입력 라벨입니다.
description / errorReactNode-설명과 오류입니다. 오류가 있으면 invalid 상태를 적용합니다.
required / disabledboolean-라벨 표시와 입력 상태에 함께 전달합니다.
id / controlIdstring-입력 ID입니다. 생략하면 자동 생성합니다.
controlReactElement-기존 control prop도 지원합니다. children과 둘 중 하나만 지정합니다.

Examples

자주 쓰는 조합을 살펴보고, 필요한 예시의 코드를 펼쳐 확인하세요.

RHF register로 연결

register는 내부 Input에, description과 errors.name?.message는 FormField에 지정합니다. 검증 규칙은 register가 소유하고 FormField는 오류 표시와 ARIA 연결을 담당합니다. noValidate로 브라우저 기본 오류 팝업 대신 RHF 메시지를 표시합니다. required 표시는 register의 검증 규칙과 별도로 지정합니다.

두 글자 이상 입력해 주세요.

import { useState } from 'react'

import { useForm } from 'react-hook-form'

import { FormField, Input, Button } from '@amineslab/ui'

function RegisterFormFieldExample() {
    const [saved, setSaved] = useState(false);
    const { register, handleSubmit, formState: { errors }, } = useForm({
        defaultValues: { name: '' },
        mode: 'onBlur',
    });
    return (<form noValidate className="grid w-full gap-4" onSubmit={handleSubmit(() => setSaved(true))} onChange={() => setSaved(false)}>
      <FormField label="이름" required description="두 글자 이상 입력해 주세요." error={errors.name?.message}>
        <Input {...register('name', {
        required: '이름을 입력해 주세요.',
        minLength: { value: 2, message: '두 글자 이상 입력해 주세요.' },
    })}/>
      </FormField>
      <Button type="submit">{saved ? '저장됨' : '저장'}</Button>
    </form>);
}

export default function ExamplePreview() {
  return (<RegisterFormFieldExample />)
}

RHF Controller로 값 기반 입력 연결

NumberInput은 숫자/null을 전달하므로 Controller를 사용합니다. field의 value·onBlur·ref는 그대로, onChange는 onValueChange로 전달하고 fieldState.error?.message를 FormField에 지정합니다. 같은 입력에 register와 Controller를 함께 붙이지 않습니다.

원

100원 이상 입력해 주세요.

import { useState } from 'react'

import { useForm, Controller } from 'react-hook-form'

import { FormField, NumberInput, Button } from '@amineslab/ui'

function ControllerFormFieldExample() {
    const [saved, setSaved] = useState(false);
    const { control, handleSubmit } = useForm<{
        amount: number | null;
    }>({
        defaultValues: { amount: null },
    });
    return (<form noValidate className="grid w-full gap-4" onSubmit={handleSubmit(() => setSaved(true))} onChange={() => setSaved(false)}>
      <Controller control={control} name="amount" rules={{
            validate: (value) => (value !== null && value >= 100) || '100원 이상 입력해 주세요.',
        }} render={({ field: { onChange, ...field }, fieldState }) => (<FormField label="금액" required description="100원 이상 입력해 주세요." error={fieldState.error?.message}>
            <NumberInput {...field} onValueChange={onChange} endAdornment="원"/>
          </FormField>)}/>
      <Button type="submit">{saved ? '저장됨' : '저장'}</Button>
    </form>);
}

export default function ExamplePreview() {
  return (<ControllerFormFieldExample />)
}

기본 입력에 라벨과 설명 붙이기

FormField 안에는 Input·NumberInput·Textarea 같은 기본 입력 하나를 넣습니다. TextField·NumberField처럼 이미 라벨을 가진 Field 계열은 직접 사용하세요.

원 단위로 입력해 주세요

import { FormField, NumberInput, Textarea, Input } from '@amineslab/ui'

function formFieldPlaceholder(control: string) {
    return control === 'NumberInput' ? '금액을 입력해 주세요' : '내용을 입력해 주세요';
}

function FormFieldExample({ label = '프로젝트 이름', description = '팀에서 식별할 이름입니다.', required = false, error = false, disabled = false, control = 'Input', }: {
    label?: string;
    description?: string;
    required?: boolean;
    error?: boolean;
    disabled?: boolean;
    control?: string;
}) {
    return (<FormField label={label} description={description} required={required} disabled={disabled} error={error ? '입력 내용을 확인해 주세요.' : undefined}>
      {control === 'NumberInput' ? (<NumberInput placeholder={formFieldPlaceholder(control)}/>) : control === 'Textarea' ? (<Textarea placeholder={formFieldPlaceholder(control)}/>) : (<Input placeholder={formFieldPlaceholder(control)}/>)}
    </FormField>);
}

export default function ExamplePreview() {
  return ((<FormFieldExample label="예산" description="원 단위로 입력해 주세요" required control="NumberInput"/>))
}

사용 기준

권장하는 사용
  • Input·NumberInput·Textarea 같은 기본 입력 하나를 children으로 넣습니다.
  • id와 라벨·설명·오류의 접근성 관계를 자동 연결합니다.
  • RHF의 register와 검증 규칙은 기본 입력에 연결하고 오류 문자열은 FormField의 error로 전달합니다.
  • 설명은 오류가 표시되어도 유지됩니다.
  • TextField·NumberField 같은 Field 계열은 이미 이 역할을 포함하므로 FormField 안에 중첩하지 않습니다.
피해야 할 사용
  • 겉모양만으로 사용을 결정하지 마세요.
  • 의미와 키보드 동작, 모바일에서의 흐름을 함께 확인하세요.

접근성

화면에 맞는 이름을 제공하고, 키보드만으로 작업을 마칠 수 있는지 확인하세요.

  • Tab: 내부 기본 입력으로 이동합니다.