Better Upload

Upload Button

A button that uploads a single file.

Demo

Installation

npx shadcn@latest add "https://better-upload.com/r/upload-button.json"

Install the following dependencies:

npm install lucide-react

Also add the shadcn/ui button component to your project. As the upload button is built on top of it.

Copy and paste the following code into your project.

import { Button } from '@/components/ui/button';
import type { UploadHookControl } from 'better-upload/client';
import { Loader2, Upload } from 'lucide-react';
import { useId } from 'react';

type UploadButtonProps = {
  control: UploadHookControl<false>;
  accept?: string;
  metadata?: Record<string, unknown>;
  uploadOverride?: (
    ...args: Parameters<UploadHookControl<false>['upload']>
  ) => void;

  // Add any additional props you need.
};

export function UploadButton({
  control: { upload, isPending },
  accept,
  metadata,
  uploadOverride,
}: UploadButtonProps) {
  const id = useId();

  return (
    <Button disabled={isPending} className="relative" type="button">
      <label htmlFor={id} className="absolute inset-0 cursor-pointer">
        <input
          id={id}
          className="absolute inset-0 size-0 opacity-0"
          type="file"
          accept={accept}
          onChange={(e) => {
            if (e.target.files?.[0] && !isPending) {
              if (uploadOverride) {
                uploadOverride(e.target.files[0], { metadata });
              } else {
                upload(e.target.files[0], { metadata });
              }
            }
            e.target.value = '';
          }}
        />
      </label>
      {isPending ? (
        <>
          <Loader2 className="size-4 animate-spin" />
          Upload file
        </>
      ) : (
        <>
          <Upload className="size-4" />
          Upload file
        </>
      )}
    </Button>
  );
}

Update the import paths to match your project setup.

Usage

The <UploadButton /> should be used with the useUploadFile hook.

'use client';

import { useUploadFile } from 'better-upload/client';
import { UploadButton } from '@/components/ui/upload-button';

export function Uploader() {
  const { control } = useUploadFile({
    route: 'demo',
  });

  return <UploadButton control={control} accept="image/*" />;
}

The button will open a file picker dialog when clicked, and upload the selected file to the desired route.

Props

PropTypeDefault
control
object
-
accept?
string | undefined
-
metadata?
Record<string, unknown> | undefined
-
uploadOverride?
function | undefined
-