Active Foundation · Example
Two files, to open or remove
Two files, to open or removeClient
2 files attached
Full source examples/ui/attachment.tsx
// apps/docs/examples/ui/attachment.tsx'use client';
import {useState} from 'react';import {FileSpreadsheetIcon, FileTextIcon, XIcon} from 'lucide-react';
import {formatByteSize} from '@workspace/active-format';import { Attachment, AttachmentAction, AttachmentActions, AttachmentContent, AttachmentDescription, AttachmentGroup, AttachmentMedia, AttachmentTitle, AttachmentTrigger,} from '@workspace/active-ui/components/primitives/attachment';
const FILES = [ {name: 'participants.csv', bytes: 48_213, icon: FileSpreadsheetIcon}, {name: 'scheme-rules-2026.pdf', bytes: 1_204_331, icon: FileTextIcon},];
/** * Files attached to a message or a form, as compact cards: an icon, the * name, the size (written by active-format), a trigger over the whole card * that opens the file, and an action that removes it. */export default function AttachmentExample() { const [files, setFiles] = useState(FILES); const [opened, setOpened] = useState<string | null>(null); return ( <div className="flex w-full max-w-md flex-col gap-3"> <AttachmentGroup> {files.map((file) => ( <Attachment key={file.name}> <AttachmentTrigger aria-label={`Open ${file.name}`} onClick={() => setOpened(file.name)} /> <AttachmentMedia> <file.icon aria-hidden /> </AttachmentMedia> <AttachmentContent> <AttachmentTitle>{file.name}</AttachmentTitle> <AttachmentDescription> {formatByteSize(file.bytes, 'en', {system: 'decimal'})} </AttachmentDescription> </AttachmentContent> <AttachmentActions> <AttachmentAction aria-label={`Remove ${file.name}`} onClick={() => setFiles((current) => current.filter((other) => other.name !== file.name), ) } > <XIcon aria-hidden /> </AttachmentAction> </AttachmentActions> </Attachment> ))} </AttachmentGroup> <p className="text-muted-foreground text-sm" aria-live="polite"> {opened ? `Opened ${opened}` : `${files.length} files attached`} </p> </div> );}