Files

Complete UI system for file uploading and management.

Overview

A complete file management system for browsing, selecting, and uploading files.

Use Files for the full compound UI, or Files.Provider plus lower-level pieces when you need a custom layout.

Key Concepts

Raw Rows In, UI Out

Pass your raw FileAsset rows. Files maps them to UI labels, icons, badges, and status display.

Upload Progress Overlay

Pass UploadProgressState (or UploadProgressLike) from your upload state. Active progress entries (0-100) override row status until complete.

Composable Components

  • Files: full compound file manager
  • Files.Provider: headless state provider for custom layouts
  • FileDetails: composable details panel
  • FileListView, FileGridView: browse primitives

Data Contract

FileAsset

FileAsset is a discriminator union keyed by asset_type.

type FileAsset = StandardFileAsset | VirtualFileAsset;

type FileAssetBase = {
  id: string;
  name: string;
  status: "active" | "archived" | "waiting" | "pending" | "failed";
  created_at: Date | string | null;
  updated_at: Date | string | null;
  created_by: string | null;
  created_by_image_url?: string | null;
  indicators?: FileIndicator[];
  icon?: React.ReactNode;
};

type StandardFileAsset = FileAssetBase & {
  asset_type: "standard";
  mime_type: string;
  size: number | bigint | string | null;
};

type VirtualFileAsset = FileAssetBase & {
  asset_type: "virtual";
  virtual_type: string;
};

What this means for consumers:

  • asset_type: "standard" requires mime_type and size.
  • asset_type: "virtual" requires virtual_type.
  • Do not mix standard and virtual-only fields on the same row.

FileIndicator

type FileIndicator = {
  label: string;
  icon: React.ReactNode;
};

Indicators render as compact badges on list/grid items (up to 2).

Upload Progress

type UploadProgressState = {
  percentage: number; // 0-100
  filesCompleted: number;
  totalFiles: number;
  fileProgress: Map<
    string,
    {
      id: string;
      percentage: number; // 0-100
      status: "waiting" | "pending" | "uploading" | "completed" | "failed";
      etaSeconds?: number;
    }
  >;
};

Virtual Files

Use virtual rows for derived content (for example transcripts or summaries) that does not map to a real uploaded blob.

import { Scroll } from "@tilt-legal/cubitt-icons/ui/outline";

const transcript: FileAsset = {
  id: "virtual-1",
  name: "Meeting Notes Transcript",
  asset_type: "virtual",
  virtual_type: "transcript",
  status: "active",
  created_by: "System",
  created_at: "2025-01-10T09:00:00.000Z",
  updated_at: "2025-01-10T09:00:00.000Z",
  icon: <Scroll />,
};

Virtual files often shouldn't be renameable. Use the canRename predicate on Files to disable rename for specific files:

<Files
  files={files}
  onRename={handleRename}
  canRename={(file) => !file.isVirtual}
>

On this page