Files
Utilities for file type detection, categorization, and formatting.
Related component
For displaying file type icons, see MimeTypeIcon.
Overview
The file utilities provide a unified API for working with file types. All functions accept either a filename (uses extension) or MIME type string, making them flexible for different use cases.
File type detection and categorization live in utilities, while display formatting functions live in utilities/formatters:
// Detection, categorization, and type guards
import {
getFileInfo,
getFileCategory,
getFileExtension,
isImage,
isPreviewable,
FILE_CATEGORIES,
} from "@tilt-legal/cubitt-components/utilities";
// Display formatting
import {
getFileLabel,
getExtensionLabel,
} from "@tilt-legal/cubitt-components/utilities/formatters";Main API
getFileInfo
Returns complete file information in a single call. Use this when you need multiple pieces of information about a file.
import { getFileInfo } from "@tilt-legal/cubitt-components/utilities";
getFileInfo("document.pdf");
// { category: "document", label: "PDF Document", extension: "pdf" }
getFileInfo("photo.png", "image/png");
// { category: "image", label: "PNG Image", extension: "png" }
getFileInfo(undefined, "application/pdf");
// { category: "document", label: "PDF Document", extension: "" }| Property | Type | Description |
|---|---|---|
category | FileCategory | Category for grouping (e.g., "image") |
label | string | Human-readable label (e.g., "PNG Image") |
extension | string | Lowercase extension without dot, or empty |
File details component
function FileDetails({ file }: { file: File }) {
const { category, label, extension } = getFileInfo(file.name, file.type);
return (
<div className="flex items-center gap-3">
<MimeTypeIcon mimeType={file.type} className="size-8" />
<div>
<p className="font-medium">{file.name}</p>
<p className="text-sm text-muted-foreground">
{label}{" "}
{extension && (
<Badge variant="secondary" appearance="outline">{extension.toUpperCase()}</Badge>
)}
</p>
</div>
</div>
);
}Accessor Functions
getFileCategory
Returns the file category for grouping and filtering.
import { getFileCategory } from "@tilt-legal/cubitt-components/utilities";
getFileCategory("photo.png"); // "image"
getFileCategory("application/pdf"); // "document"
getFileCategory("document.docx"); // "document"
getFileCategory("video/mp4"); // "video"
getFileCategory("unknown-file"); // "unknown"| Category | Extensions |
|---|---|
image | png, jpg, gif, svg, webp, heic, etc |
video | mp4, mov, avi, webm, mkv, etc |
audio | mp3, wav, ogg, flac, aac, etc |
document | pdf, doc, docx, odt, rtf |
spreadsheet | xls, xlsx, csv, ods |
presentation | ppt, pptx, odp |
archive | zip, rar, 7z, tar, gz |
code | js, ts, py, java, json, xml, etc |
text | txt, md, html |
email | eml, msg |
unknown | Unrecognized types |
getFileLabel
Returns a human-readable description suitable for display.
import { getFileLabel } from "@tilt-legal/cubitt-components/utilities/formatters";
getFileLabel("photo.png"); // "PNG Image"
getFileLabel("application/pdf"); // "PDF Document"
getFileLabel("document.docx"); // "Word Document"
getFileLabel("data.xlsx"); // "Excel Spreadsheet"With MimeTypeIcon
<div className="flex items-center gap-2">
<MimeTypeIcon mimeType={file.type} className="size-5" />
<span className="text-muted-foreground">
{getFileLabel(file.name, file.type)}
</span>
</div>
// Displays: [icon] "PDF Document"getFileExtension
Extracts the file extension from a filename. Returns lowercase without the leading dot.
import { getFileExtension } from "@tilt-legal/cubitt-components/utilities";
getFileExtension("document.pdf"); // "pdf"
getFileExtension("photo.JPEG"); // "jpeg"
getFileExtension("archive.tar.gz"); // "gz"
getFileExtension(".gitignore"); // "gitignore"
getFileExtension("README"); // ""getExtensionLabel
Returns an uppercase extension label for badges and compact displays. Falls back to MIME type if filename has no extension.
import { getExtensionLabel } from "@tilt-legal/cubitt-components/utilities/formatters";
getExtensionLabel("document.pdf"); // "PDF"
getExtensionLabel(undefined, "image/png"); // "PNG"
getExtensionLabel("file", "application/pdf"); // "PDF"Extension badge
const ext = getExtensionLabel(file.name, file.type);
{
ext && (
<Badge variant="secondary" appearance="outline" className="text-xs">
{ext}
</Badge>
);
}resolveExtension
Resolves a filename and/or MIME type to a canonical file extension. Tries the filename extension first, then resolves the MIME type through the internal registry.
import { resolveExtension } from "@tilt-legal/cubitt-components/utilities";
resolveExtension("report.pdf"); // "pdf"
resolveExtension(undefined, "audio/x-m4a"); // "m4a"
resolveExtension("file.bin", "application/pdf"); // "bin"
resolveExtension(undefined, "application/octet-stream"); // ""Type Guards
Functions for checking file types. All accept string | null | undefined and return boolean.
isImage, isVideo, isAudio
Check if MIME type matches a specific media category.
import { isImage, isVideo, isAudio } from "@tilt-legal/cubitt-components/utilities";
isImage("image/png"); // true
isImage("video/mp4"); // false
isVideo("video/mp4"); // true
isAudio("audio/mpeg"); // trueisMedia
Returns true if the MIME type is image, video, or audio.
import { isMedia } from "@tilt-legal/cubitt-components/utilities";
isMedia("image/png"); // true
isMedia("video/mp4"); // true
isMedia("audio/mpeg"); // true
isMedia("application/pdf"); // falseisPreviewable
Returns true if the file can be previewed in a browser (images, videos, PDFs).
import { isPreviewable } from "@tilt-legal/cubitt-components/utilities";
isPreviewable("image/png"); // true
isPreviewable("video/mp4"); // true
isPreviewable("application/pdf"); // true
isPreviewable("text/plain"); // falseConditional preview
{
isPreviewable(file.type) ? (
<FilePreview file={file} />
) : (
<FileDownloadButton file={file} />
);
}fileIs
Namespaced object containing all type guards.
import { fileIs } from "@tilt-legal/cubitt-components/utilities";
fileIs.image("image/png"); // true
fileIs.video("video/mp4"); // true
fileIs.previewable("image/png"); // trueConstants
FILE_CATEGORIES
Readonly array of all available file categories. Useful for building filter UIs or validation.
import { FILE_CATEGORIES } from "@tilt-legal/cubitt-components/utilities";
// ["image", "video", "audio", "document", "spreadsheet", "presentation", "archive", "code", "text", "email", "unknown"]Filter dropdown
const fileCategoryItems = FILE_CATEGORIES.filter((category) => category !== "unknown").map(
(category) => ({
label: `${category.charAt(0).toUpperCase() + category.slice(1)}s`,
value: category,
})
);
<Select
items={[{ label: "All files", value: "all" }, ...fileCategoryItems]}
value={filter}
onValueChange={setFilter}
>
<SelectTrigger>
<SelectValue placeholder="All files" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All files</SelectItem>
{fileCategoryItems.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectContent>
</Select>Types
FileCategory
Union type of all possible file categories.
import type { FileCategory } from "@tilt-legal/cubitt-components/utilities";
type FileCategory =
| "image"
| "video"
| "audio"
| "document"
| "spreadsheet"
| "presentation"
| "archive"
| "code"
| "text"
| "email"
| "unknown";FileInfo
Return type of getFileInfo().
import type { FileInfo } from "@tilt-legal/cubitt-components/utilities";
type FileInfo = {
category: FileCategory;
label: string;
extension: string;
};Examples
File list with categories
function FileList({ files }: { files: File[] }) {
return (
<div className="space-y-2">
{files.map((file) => (
<div key={file.name} className="flex items-center gap-3">
<MimeTypeIcon mimeType={file.type} className="size-5" />
<span className="flex-1 truncate">{file.name}</span>
<Badge variant="secondary">{getFileCategory(file.type)}</Badge>
</div>
))}
</div>
);
}Grouped file browser
function GroupedFiles({ files }: { files: File[] }) {
const grouped = files.reduce(
(acc, file) => {
const category = getFileCategory(file.name, file.type);
acc[category] = [...(acc[category] || []), file];
return acc;
},
{} as Record<FileCategory, File[]>,
);
return (
<div className="space-y-6">
{Object.entries(grouped).map(([category, files]) => (
<div key={category}>
<h3 className="font-medium capitalize mb-2">{category}s</h3>
<div className="space-y-1">
{files.map((file) => (
<FileRow key={file.name} file={file} />
))}
</div>
</div>
))}
</div>
);
}File details sheet
function FileDetailsSheet({ file }: { file: FileWithMeta }) {
const info = getFileInfo(file.name, file.mimeType);
return (
<SheetContent>
<SheetHeader>
<SheetTitle>{file.name}</SheetTitle>
</SheetHeader>
<div className="space-y-4 py-4">
<div className="flex items-center gap-4">
<MimeTypeIcon mimeType={file.mimeType} className="size-12" />
<div>
<p className="font-medium">{info.label}</p>
<p className="text-sm text-muted-foreground">
{formatBytes(file.size)}
</p>
</div>
</div>
{isPreviewable(file.mimeType) && <FilePreview file={file} />}
</div>
</SheetContent>
);
}Media gallery filter
function MediaGallery({ files }: { files: FileWithMeta[] }) {
const [filter, setFilter] = useState<"all" | "image" | "video">("all");
const filtered = files.filter((file) => {
if (filter === "all") return isMedia(file.mimeType);
if (filter === "image") return isImage(file.mimeType);
if (filter === "video") return isVideo(file.mimeType);
return false;
});
return (
<div>
<ToggleGroup
type="single"
value={filter}
onValueChange={(v) => setFilter(v as typeof filter)}
>
<ToggleGroupItem value="all">All</ToggleGroupItem>
<ToggleGroupItem value="image">Images</ToggleGroupItem>
<ToggleGroupItem value="video">Videos</ToggleGroupItem>
</ToggleGroup>
<div className="grid grid-cols-3 gap-2 mt-4">
{filtered.map((file) => (
<MediaThumbnail key={file.id} file={file} />
))}
</div>
</div>
);
}