Better Upload

Upload Button

A button that uploads a single file.

Preview

Installation

npx shadcn@latest add @better-upload/upload-button

Install the following dependencies:

npm i 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.

components/ui/upload-button.tsx
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>;
  id?: string;
  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 },
  id: _id,
  accept,
  metadata,
  uploadOverride,
}: UploadButtonProps) {
  const id = useId();

  return (
    <Button disabled={isPending} className="relative" type="button">
      <label htmlFor={_id || id} className="absolute inset-0 cursor-pointer">
        <input
          id={_id || id}
          disabled={isPending}
          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 /> component 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: 'profile',
  });

  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

Prop

Type

Default

On this page

Sponsored by
Next.js Weekly

Stay up to date on Next.js

A weekly newsletter to keep up with what's happening in the ecosystem.

Need software licensing?

Simple licensing for software, easily distribute your product.

  • - payment automation
  • - customer portal
  • - offline licensing