Skip to content
v1.3.1
Fit

File Upload

A drag-and-drop dropzone with a click-to-browse fallback and a list of selected files. The HTML version (file-upload.js) opens the picker, highlights on drag, and renders picked files as rows; React/Vue add a controlled files list so you can drive real upload progress and status.

bash
jlds add file-upload

Usage

Drop files on the zone or click to browse. Picked files appear below with a remove button.

html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jarooda/jlds@main/registry/css/index.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jarooda/jlds@main/registry/css/file-upload.css">
<!-- behavior layer: open picker, drag highlight, render rows -->
<script src="https://cdn.jsdelivr.net/gh/jarooda/jlds@main/registry/js/core.js" defer></script>
<script src="https://cdn.jsdelivr.net/gh/jarooda/jlds@main/registry/js/util.js" defer></script>
<script src="https://cdn.jsdelivr.net/gh/jarooda/jlds@main/registry/js/file-upload.js" defer></script>

<div class="jl-upload">
  <div class="jl-upload__zone" role="button" tabindex="0">
    <span class="jl-upload__mark">
      <svg viewBox="0 0 24 24" fill="none"><path d="M12 16V4m0 0L7 9m5-5 5 5" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"/><path d="M4 16v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"/></svg>
    </span>
    <div class="jl-upload__title"><b>Click to upload</b> or drag and drop</div>
    <div class="jl-upload__hint">PNG, JPG, PDF · up to 5 MB</div>
    <input type="file" accept=".png,.jpg,.pdf" multiple style="display:none" />
  </div>
</div>
vue
<script setup lang="ts">
import { FileUpload } from "@/components/ui/file-upload"
</script>

<template>
  <FileUpload accept=".png,.jpg,.pdf" multiple :max-size="5 * 1024 * 1024"
    @files="(files) => console.log(files)" />
</template>
tsx
import { FileUpload } from "@/components/ui/file-upload"

<FileUpload
  accept=".png,.jpg,.pdf"
  multiple
  maxSize={5 * 1024 * 1024}
  onFiles={(files) => console.log(files)}
/>

With a controlled list

Pass files to render upload progress and per-file status (done, uploading, error). In HTML the rows are static markup you maintain yourself.

html
<div class="jl-upload jl-upload--sm">
  <div class="jl-upload__zone" role="button" tabindex="0"><!-- … --></div>
  <ul class="jl-upload__list">
    <li class="jl-upload__item" data-status="done">
      <span class="jl-upload__fileicon"><!-- file icon --></span>
      <div class="jl-upload__meta">
        <div class="jl-upload__name">brand-guidelines.pdf</div>
        <div class="jl-upload__size">2.4 MB</div>
      </div>
      <span class="jl-upload__status" data-status="done"><!-- check --></span>
      <button type="button" class="jl-upload__remove" aria-label="Remove"><!-- × --></button>
    </li>
    <!-- data-status="uploading" adds a .jl-upload__bar; "error" tints it red -->
  </ul>
</div>
vue
<template>
  <FileUpload
    size="sm"
    :files="[
      { name: 'brand-guidelines.pdf', size: 2_400_000, status: 'done' },
      { name: 'hero-render.png', size: 8_100_000, status: 'uploading', progress: 62 },
      { name: 'archive.zip', size: 120_000_000, status: 'error', error: 'Exceeds 50 MB limit' },
    ]"
    @remove="(i) => console.log('remove', i)"
  />
</template>
tsx
<FileUpload
  size="sm"
  files={[
    { name: "brand-guidelines.pdf", size: 2_400_000, status: "done" },
    { name: "hero-render.png", size: 8_100_000, status: "uploading", progress: 62 },
    { name: "archive.zip", size: 120_000_000, status: "error", error: "Exceeds 50 MB limit" },
  ]}
  onRemove={(i) => console.log("remove", i)}
/>

Props

React

FileUpload extends React.HTMLAttributes<HTMLDivElement> (minus onChange).

PropTypeDefaultDescription
acceptstringComma-separated accept list for the input
multiplebooleanfalseAllow selecting more than one file
maxSizenumberMax bytes per file (larger files are dropped)
disabledbooleanfalseDisable the dropzone
size"sm" | "md""md"Dropzone padding
hintReactNodeOverride the auto hint line
filesUploadFile[]Controlled list to render (progress/status)
onFiles(files: File[]) => voidFires with accepted files
onRemove(index, item) => voidFires when a row's × is clicked

UploadFile: { name, size?, status?: "done" \| "uploading" \| "error", progress?, error?, file? }.

Vue

Same options. onFiles/onRemove become the @files / @remove emits.

CSS classes (HTML)

ClassPurpose
.jl-upload + --smWrapper and compact size
.jl-upload__zone + [data-dragging] / [data-disabled]The dropzone and its states
.jl-upload__mark / __title / __hintZone icon and copy
.jl-upload__list / __item[data-status]File rows (done / uploading / error)
.jl-upload__fileicon / __meta / __name / __sizeRow icon and metadata
.jl-upload__barProgress track (shown while uploading)
.jl-upload__status / __removeStatus glyph and remove button