본문으로 건너뛰기
Amineslab UI

Component

FileInput

한 줄 필드에서 파일 선택·chip 삭제·검증을 처리합니다.

Playground

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

PC 1280px
Controls

허용 확장자 또는 MIME type · 비우면 모든 형식

파일당 최대 MB · 비우면 제한 없음

최대 파일 개수 · 비우면 제한 없음

빈 입력 안내 · 사용처의 언어와 문구로 변경

Code
import { useState } from 'react'

import { type FileItem, FileInput } from '@amineslab/ui'

function optionalLimit(value: string) {
    return value.trim() && Number.isFinite(Number(value)) ? Math.max(0, Number(value)) : undefined;
}

type FileDemoConstraints = {
    accept?: string;
    maxSize?: string;
    maxFiles?: string;
};

function FileInputExample({ disabled = false, uploading = false, invalid = false, placeholder = '파일을 선택해 주세요', accept = '.jpg,.png,.pdf', maxSize = '10', maxFiles = '4', opaque = false, multiple, size, variant, }: FileDemoConstraints & {
    disabled?: boolean;
    uploading?: boolean;
    invalid?: boolean;
    opaque?: boolean;
    placeholder?: string;
    multiple: boolean;
    size: string;
    variant: string;
}) {
    const [files, setFiles] = useState<FileItem[]>([]);
    const [error, setError] = useState('');
    const resolvedSize = size === 'xs' || size === 'sm' || size === 'lg' || size === 'xl' ? size : 'md';
    const resolvedVariant = variant === 'outline' ? 'outline' : 'filled';
    return (<div className="grid w-full max-w-lg gap-2">
      <FileInput disabled={disabled} uploading={uploading} invalid={invalid} placeholder={placeholder} opaque={opaque} aria-label="첨부 파일" accept={accept} multiple={multiple} maxFiles={optionalLimit(maxFiles)} maxSize={optionalLimit(maxSize)} size={resolvedSize} variant={resolvedVariant} value={files} onValueChange={setFiles} onError={(errors) => setError(errors[0]?.message ?? '')}/>
      {error && (<p role="alert" className="m-0 text-body-3 text-danger-700">
          {error}
        </p>)}
    </div>);
}

export default function ExamplePreview() {
  return (<FileInputExample {...({"disabled":false,"uploading":false,"invalid":false,"accept":".jpg,.png,.pdf","maxSize":"10","maxFiles":"4","placeholder":"파일을 선택해 주세요","opaque":false,"multiple":true,"size":"md","variant":"filled"} as const)}/>)
}

Props

import { FileInput, matchesAccept, formatBytes } from '@amineslab/ui'

FileInput

native file input을 controlled/uncontrolled 목록으로 감쌉니다.

NameTypeDefaultDescription
placeholderReactNode-빈 입력 안내를 사용처의 언어·어투로 덮어씁니다. 빈 문자열로 숨길 수 있습니다.
value / defaultValueFileItem[]-선택된 로컬 파일과 기존 원격 파일 목록입니다.
onValueChange(files: FileItem[]) => void-검증을 통과한 목록을 전달합니다.
acceptstring-MIME, MIME glob, 확장자를 쉼표로 지정합니다.
multiplebooleanfalse여러 파일을 허용합니다.
maxSizenumber-파일당 최대 용량(MB)입니다. 생략하면 제한하지 않습니다.
maxFilesnumber-목록 전체 최대 개수입니다.
onError(errors: FileError[]) => void-형식·크기·개수 오류를 전달합니다.
uploading / disabledboolean-진행 중 또는 비활성 상태에서 chooser와 삭제를 막습니다.
variant'filled' | 'outline''filled'필드 표면을 선택합니다.
size'xs' | 'sm' | 'md' | 'lg' | 'xl''md'필드와 chip 밀도입니다.
invalidbooleanfalse오류 테두리를 표시하며 focus·open 상태에서도 유지합니다. aria-invalid도 함께 전달합니다.
opaquebooleanfalseelevation 레이어 아래 불투명한 테마 바탕을 추가합니다. hover·focus·invalid 표현은 유지합니다.

Examples

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

공통 필드 표면

variant는 filled·outline 표현을, opaque는 elevation 아래의 불투명 바탕을 정합니다. focus와 hover는 중립 elevation을 사용하고 invalid 테두리는 포커스 중에도 유지합니다. NumberInput·PhoneInput·PasswordInput은 Input의 아이콘·문자열·ReactNode 슬롯을 상속합니다. SelectField에서는 같은 표면 props를 직접 지정하거나 triggerProps로 세부 설정할 수 있습니다.

투명 바탕
파일을 선택해 주세요
불투명 바탕
파일을 선택해 주세요
Outline + 불투명 바탕
파일을 선택해 주세요
<FileInput />

<FileInput opaque />

<FileInput variant="outline" opaque />

선택과 검증

accept·maxSize·maxFiles를 통과한 항목만 값에 추가합니다.

파일을 선택해 주세요
import { useState } from 'react'

import { type FileItem, FileInput } from '@amineslab/ui'

function optionalLimit(value: string) {
    return value.trim() && Number.isFinite(Number(value)) ? Math.max(0, Number(value)) : undefined;
}

type FileDemoConstraints = {
    accept?: string;
    maxSize?: string;
    maxFiles?: string;
};

function FileInputExample({ disabled = false, uploading = false, invalid = false, placeholder = '파일을 선택해 주세요', accept = '.jpg,.png,.pdf', maxSize = '10', maxFiles = '4', opaque = false, multiple, size, variant, }: FileDemoConstraints & {
    disabled?: boolean;
    uploading?: boolean;
    invalid?: boolean;
    opaque?: boolean;
    placeholder?: string;
    multiple: boolean;
    size: string;
    variant: string;
}) {
    const [files, setFiles] = useState<FileItem[]>([]);
    const [error, setError] = useState('');
    const resolvedSize = size === 'xs' || size === 'sm' || size === 'lg' || size === 'xl' ? size : 'md';
    const resolvedVariant = variant === 'outline' ? 'outline' : 'filled';
    return (<div className="grid w-full max-w-lg gap-2">
      <FileInput disabled={disabled} uploading={uploading} invalid={invalid} placeholder={placeholder} opaque={opaque} aria-label="첨부 파일" accept={accept} multiple={multiple} maxFiles={optionalLimit(maxFiles)} maxSize={optionalLimit(maxSize)} size={resolvedSize} variant={resolvedVariant} value={files} onValueChange={setFiles} onError={(errors) => setError(errors[0]?.message ?? '')}/>
      {error && (<p role="alert" className="m-0 text-body-3 text-danger-700">
          {error}
        </p>)}
    </div>);
}

export default function ExamplePreview() {
  return (<FileInputExample multiple size="md" variant="filled"/>)
}

FileInputField로 사용하기

FileInputField는 기본 입력에 label·required·description·error를 함께 제공합니다. FormStack 안에 직접 넣으며 FormField로 다시 감싸지 않습니다.

파일을 선택해 주세요

라벨·설명·필수 표시를 Field가 함께 처리합니다.

import { useState } from 'react'

import { type FileItem, FileInputField } from '@amineslab/ui'

const fieldDescription = '라벨·설명·필수 표시를 Field가 함께 처리합니다.';

function FileInputFieldExample() {
    const [value, setValue] = useState<FileItem[]>([]);
    return (<FileInputField label="첨부 파일" description={fieldDescription} required value={value} onValueChange={setValue} maxSize={10}/>);
}

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

사용 기준

권장하는 사용
  • FileInput은 너비에 맞는 칩과 외 N개를 한 줄에 표시합니다.
  • 첫 칩도 부족하면 파일명을 줄임표로 표시합니다.
  • 드래그앤드롭 또는 카드 미리보기가 필요하면 FileUpload를 사용합니다.
  • 값에는 local File과 remote 파일을 함께 둘 수 있습니다.
피해야 할 사용
  • 겉모양만으로 사용을 결정하지 마세요.
  • 의미와 키보드 동작, 모바일에서의 흐름을 함께 확인하세요.

접근성

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

  • Enter / Space: 파일 chooser를 엽니다. chip 삭제 버튼은 별도 포커스를 가집니다.