File Input

Pronto

Upload de arquivo com drag-and-drop, preview, validação de tipo/tamanho e progresso de upload.

Usar quando

Anexar PDF/imagem em processo, contrato, mensagem. Importar planilha (CSV). Upload de logo do escritório.

Não usar quando

Edição inline (use Editor de Documentos). Captura de foto da câmera (use input com capture).

Variantes

Arraste arquivos aqui ou clique

Arraste arquivos aqui ou clique

  • peticao-inicial.pdf
    2.4 MB · enviado
  • documentos-anexos.pdf
    8.3 MB · 65%
  • foto-prova.jpg
    4.0 MB · enviado

Arraste arquivos aqui ou clique

PDF · até 10.0 MB

Estados

Empty

Arraste arquivos aqui ou clique

Drag over (passe arquivo)

Solte para enviar

Uploading
  • contestacao-trabalhista.pdf
    5.1 MB · 42%
Uploaded
  • contrato-honorarios.pdf
    313 KB · enviado
Error (tipo inválido)
  • planilha.xlsx
    1.1 MB · Apenas PDF aceito

Microinterações

MicrointeraçãoDisparada porComportamentoTiming
Drag enterdragenter na zonaBorda dashed → solid + background brassTint150ms ease
Drop animationdrop válidoZone scale 0.98 brevemente, depois lista renderiza200ms
Upload progressXHR onProgressBarra cresce de 0% a 100% por arquivofollows network
Success morphupload completoÍcone arquivo muda pra ✓ verde + opacity full250ms
Error inlinetipo/tamanho inválidoItem ganha borda carmim + msg + ícone X150ms
Remove fileclick X num itemItem desliza horizontal + opacity 0200ms
Click to browseclick na zona vaziaAbre file picker nativoinstant

Acessibilidade

Acessibilidade — checklist

Teclado
TabFoco na zona
Enter / SpaceAbre file picker
Tab → XFoco em remover (lista)
Enter no XRemove arquivo
ARIA esperado
  • role="button" aria-label="Selecionar arquivo"
  • aria-describedby="hint" para tipos aceitos
  • <input type="file" hidden /> com onChange
  • Lista: <ul role="list"><li><button aria-label="Remover X" />
Notas
  • Drag-and-drop é nice-to-have — sempre suporte click para abrir picker.
  • Validação visual e aria: tipo errado não pode passar silenciosamente.
  • Mostre tipos aceitos e tamanho máximo no hint visível.
  • Em mobile, drag-and-drop não existe — picker nativo abre com Enter.

Código

'use client';
import { useRef, useState } from 'react';
import { Upload } from 'lucide-react';

export function FileInput({ accept, maxBytes, multiple, onFiles }: Props) {
  const ref = useRef<HTMLInputElement>(null);
  const [drag, setDrag] = useState(false);

  function handleFiles(list: FileList | null) {
    if (!list) return;
    const files = Array.from(list).filter(f => {
      if (accept && !accept.includes(f.type)) return false;
      if (maxBytes && f.size > maxBytes) return false;
      return true;
    });
    onFiles(files);
  }

  return (
    <div
      onClick={() => ref.current?.click()}
      onDragOver={(e) => { e.preventDefault(); setDrag(true); }}
      onDragLeave={() => setDrag(false)}
      onDrop={(e) => { e.preventDefault(); setDrag(false); handleFiles(e.dataTransfer.files); }}
      role="button" tabIndex={0}
      style={{
        border: `1.5px dashed ${drag ? T.borderInk : T.border}`,
        background: drag ? T.brassTint : T.surface,
        borderRadius: RADIUS.lg,
        padding: SP[8],
        textAlign: 'center',
        cursor: 'pointer',
      }}
    >
      <Upload size={20} />
      <p>Arraste arquivos ou clique</p>
      <input ref={ref} type="file" multiple={multiple} accept={accept?.join(',')}
        hidden onChange={(e) => handleFiles(e.target.files)} />
    </div>
  );
}

Regras

Faça

  • Suporte drag-and-drop + click para picker nativo.
  • Mostre tipos aceitos e tamanho máximo visíveis.
  • Lista com nome + tamanho + ícone do tipo + remover.
  • Progresso por arquivo durante upload.
  • Validação imediata: tipo errado/tamanho excedido = erro inline antes do upload.
  • Em mobile, picker nativo é suficiente.

Não faça

  • Não dependa só de drag-and-drop (não funciona em mobile).
  • Não silencie erros (tipo errado deve aparecer claro).
  • Não bloqueie UI durante upload — mostre progresso e deixe usuário continuar.
  • Não remova arquivo sem confirmação visual (animação ajuda).
  • Não permita upload sem feedback de progresso.

Tokens usados

TokenValorPapel
T.surface#FFFFFFbackground da zona
T.border#E3DFD2borda dashed default
T.borderInk#000000borda quando drag-over
T.brassTintrgba(164,124,43,0.08)background quando drag-over
T.verde#2F6B3Cícone success
T.carmim#A32E2Eerro de tipo/tamanho
RADIUS.lg18borderRadius da zona