Skip to content
Template Empire
Browse components

Download Progress Button

A free download progress button for Next.js and React with determinate, cancel, success and retry states.

Determinate transfer

Start the download, or cancel it midway to reveal the retry state.

Customise

184px
48px
3px
10px
420ms

Code

<BtnDownloadProgress />

Download Progress Button is a free button component for Next.js and React, styled with Tailwind, that presents a determinate file transfer across idle, downloading, success and error states. Pass a controlled percentage and status, then connect the primary and optional cancel actions to your own download logic.

The fixed footprint prevents layout shift while Motion swaps one icon and one label at a time, scales the progress fill from the left, draws the success check and nudges the error state once. A native cancel button remains independently keyboard reachable, the active percentage is exposed as a progressbar, success and error are politely announced, and reduced motion makes every state update instant and static.

Install

Add the @te registry to your components.json once:

{
  "registries": {
    "@te": "https://templateempire.io/r/{name}.json"
  }
}

Then install the component:

npx shadcn@latest add @te/btn-download-progress

Usage

"use client";

import { useState } from "react";

import { BtnDownloadProgress } from "@/components/btn-download-progress";

type DownloadStatus = "idle" | "downloading" | "success" | "error";

export default function DownloadButton() {
  const [status, setStatus] = useState<DownloadStatus>("idle");
  const [progress, setProgress] = useState(0);

  return (
    <BtnDownloadProgress
      progress={progress}
      status={status}
      onAction={() => {
        setProgress(0);
        setStatus("downloading");
      }}
      onCancel={() => setStatus("error")}
    />
  );
}

Props

PropTypeDefaultDescription
progressnumber0Determinate value clamped to 0 through 100.
status"idle" | "downloading" | "success" | "error""idle"Controlled visual and semantic state.
labelstring"Download"Idle visible label.
downloadingLabelstring"Downloading"Active visible label with the percentage appended.
successLabelstring"Downloaded"Completed visible label and polite announcement.
errorLabelstring"Try again"Error visible label and polite announcement.
widthnumber184CSS pixel width clamped to 144 through 280.
heightnumber48CSS pixel height clamped to 40 through 64.
progressHeightnumber3Track height in CSS pixels, clamped to 2 through 8.
iconTravelnumber10Vertical icon travel in CSS pixels, clamped to 0 through 18.
settleDurationnumber420Success draw and error settle time in milliseconds, clamped to 180 through 900.
disabledbooleanfalseUses native button disabled behaviour for both actions.
onAction() => voidundefinedStarts an idle download or retries an error.
onCancel() => voidundefinedInvoked from the independently focusable active cancel button.
classNamestringundefinedAdditional classes merged onto the fixed-size root group.

Customisation

Download Progress Button exposes three Tier 3 colour tokens. The fill token paints determinate progress and the success icon, the track token paints the inactive rail, and the error token paints the retry state. Override them on any ancestor while geometry and timing remain available through the component props:

<div
  style={{
    "--download-progress-fill": "var(--primary)",
    "--download-progress-track": "var(--muted)",
    "--download-progress-error": "var(--destructive)",
  }}
>
  <BtnDownloadProgress
    progress={progress}
    status={status}
    width={208}
    progressHeight={4}
    onAction={startDownload}
    onCancel={cancelDownload}
  />
</div>

Frequently asked questions

Is Download Progress Button free for commercial use?
Yes. Download Progress Button is MIT licensed, so you can use it in personal and commercial projects without attribution.
Is the progress button accessible and reduced-motion safe?
Yes. The primary and cancel actions are separate native buttons with visible focus rings, downloading exposes a named determinate progressbar, and a polite live region announces success or error once. Motion changes only transforms and opacity inside a fixed footprint. With reduced motion enabled, the percentage and states remain fully usable but every swap, icon movement, path draw and error nudge becomes instant.
How do I customise the progress track and timing?
Set width, height and progressHeight for the fixed geometry, iconTravel for the vertical icon movement, and settleDuration for the success or error beat. Retint the fill, track and error accent with the three component tokens, which default to your primary, muted and destructive semantic colours.

Related components

Want the full page this belongs to?Template Empire builds complete Next.js templates with polished async states throughout. Browse the paid templates that pair with these free components.Explore Template Empire templates