BuildUpload componentsAll docs

documentation / build

Upload components

Style the upload button and dropzone, or build your own picker on the upload hook.

Stylesheet

app/layout.tsx
import "@uploadfile/react/styles.css";

Import the stylesheet once. Vue, Svelte, and Solid ship the same file at @uploadfile/vue/styles.css, @uploadfile/svelte/styles.css, and @uploadfile/solid/styles.css. The Nuxt module adds it for you. Skip the stylesheet if you use Tailwind CSS.

Tailwind CSS

app/globals.css (Tailwind v4)
@import "tailwindcss";
@import "@uploadfile/core/tw/v4";
@source "../node_modules/@uploadfile/react/dist";
tailwind.config.ts (Tailwind v3)
import { withUf } from "@uploadfile/core/tw";

export default withUf({
  content: ["./app/**/*.{ts,tsx}"],
});

Both setups add the uf-* variants and tell Tailwind to scan the component package. For v4, point @source at the package you use, relative to the CSS file.

Target elements and states

Each component marks its parts with data-uf-element and its state with data-state. The Tailwind variants uf-button, uf-allowed-content, uf-label, uf-upload-icon, and uf-clear-btn target the parts. uf-readying, uf-ready, and uf-uploading target the states.

app/image-upload.tsx
<UploadButton
  endpoint="imageUploader"
  className="uf-button:bg-violet-600 uf-uploading:opacity-70 uf-allowed-content:text-zinc-500"
/>

Appearance and content

appearance sets classes or styles per part, and content replaces the text or elements inside each part. Both accept a function of the component state, which includes ready, isUploading, uploadProgress, and files.

app/image-dropzone.tsx
<UploadDropzone
  endpoint="imageUploader"
  appearance={{
    container: "rounded-xl border border-dashed p-8",
    button: ({ isUploading }) => (isUploading ? "opacity-60" : ""),
  }}
  content={{
    label: "Drop images here",
    allowedContent: ({ ready }) => (ready ? "Up to 4 images, 4 MB each" : "Loading..."),
  }}
/>

Build your own picker

app/avatar-picker.tsx
"use client";

import { generateReactHelpers } from "@uploadfile/react";
import type { AppFileRouter } from "./api/uploadfile/core";

const { useUploadfile } = generateReactHelpers<AppFileRouter>();

export function AvatarPicker() {
  const { startUpload, isUploading } = useUploadfile("imageUploader", {
    onClientUploadComplete: (files) => console.log(files[0]?.ufsUrl),
    onUploadError: (error) => alert(error.message),
  });

  return (
    <input
      type="file"
      accept="image/*"
      disabled={isUploading}
      onChange={(event) => {
        const files = Array.from(event.target.files ?? []);
        if (files.length > 0) void startUpload(files);
      }}
    />
  );
}

startUpload runs the same checks and transfer as the components. The Vue, Svelte, and Solid packages provide the same hook as useUploadfile or createUploadfile.

Up nextServer APIUpload, list, rename, sign, and delete files from trusted server code.