Upload Progress

Per-file and aggregate progress bars for in-flight uploads, reading the ambient state of a useFiles instance.

A drop-in progress view. It reads the ambient uploads and progress a useFiles instance exposes, so it lights up for any upload that instance starts — wherever the call originates. Returns null when idle.

"use client";import { useFiles } from "files-sdk/react";import { UploadIcon } from "lucide-react";import type { ChangeEvent } from "react";import { useEffect, useRef } from "react";import { Button } from "@/components/ui/button";import { UploadProgress } from "@/components/files-sdk/upload-progress";const Example = () => {  const files = useFiles({ endpoint: "/api/files" });  const inputRef = useRef<HTMLInputElement>(null);  // Read `files` via a ref so this one-shot effect never re-runs on store  // changes (see the file-list/file-preview note on the loop footgun).  const filesRef = useRef(files);  filesRef.current = files;  // Kick off a sample upload on mount so the progress UI starts populated.  useEffect(() => {    const bytes = new Uint8Array(3 * 1024 * 1024);    const sample = new File([bytes], "vacation-photo.jpg", {      type: "image/jpeg",    });    void filesRef.current.upload(sample);  }, []);  const handleChange = async (event: ChangeEvent<HTMLInputElement>) => {    // Capture the element now — React nulls `currentTarget` after the handler's    // synchronous phase, so it's gone by the time the uploads below resolve.    const input = event.currentTarget;    const picked = input.files;    if (!picked?.length) {      return;    }    for (const file of picked) {      await files.upload(`demo/${file.name}`, file, {        contentType: file.type,      });    }    input.value = "";  };  return (    <div className="flex flex-col gap-4">      <div>        <Button          onClick={() => inputRef.current?.click()}          type="button"          variant="outline"        >          <UploadIcon />          Choose files        </Button>        <input          aria-label="Choose files to upload"          className="hidden"          multiple          onChange={(event) => void handleChange(event)}          ref={inputRef}          type="file"        />      </div>      <UploadProgress files={files} />    </div>  );};export default Example;

Installation

npx shadcn@latest add https://files-sdk.dev/r/upload-progress.json
pnpm dlx shadcn@latest add https://files-sdk.dev/r/upload-progress.json
bunx --bun shadcn@latest add https://files-sdk.dev/r/upload-progress.json

Usage

import { useFiles } from "files-sdk/react";

import { UploadProgress } from "@/components/files-sdk/upload-progress";

export function Uploader() {
  const files = useFiles({ endpoint: "/api/files" });

  return (
    <>
      <button onClick={() => files.upload(/* … */)} type="button">
        Upload
      </button>
      <UploadProgress files={files} />
    </>
  );
}

Each row shows a file's name, percentage and status (uploading, success, error, aborted). When more than one file is in flight, an aggregate bar is shown on top.

Props

Prop

Type

On this page