Glin UI
Primitive / Moleculebeta

Attachment

File chip or image thumbnail with type icon, size, upload progress and remove button.

Edit on GitHub

Attached to this message

q3-revenue.csv47 KB
board-notes.pdf1.1 MB
architecture.pngUploading 64%
export.zip5.1 MB

Installation

Install from
pnpm dlx @glinui/cli@latest add attachment

Prefer the package?

Usage

In a message

Attachments inside a user message.

Q3 revenue review

Glin assistant, 3 sources

Online
MK
Summarize how Q3 went compared with Q2.
q3-revenue.csv47 KB
board-notes.pdf1.1 MB
G
Revenue grew 12% quarter over quarter, driven mostly by the Pro plan. Churn stayed flat at 1.8%, so the gain came from expansion rather than new logos.
MK
Which regions grew fastest?
G
Europe led with 19% growth, followed by North America at 11%. APAC was flat after the pricing change in August.
G
Thinking...
import { ArrowsClockwise, Paperclip, ThumbsDown, ThumbsUp } from "@phosphor-icons/react"
import {
Attachment, Badge, Heading, Message, MessageAction, MessageActions, MessageAvatar, MessageContent,
MessageCopyAction, MessageScroller, PromptInput, StreamingText, Text, Thinking
} from "@glinui/ui"
const reply = "Revenue grew 12% quarter over quarter, driven mostly by the Pro plan. Churn stayed flat at 1.8%, so the gain came from expansion rather than new logos."
export function ChatScreen() {
return (
<div className="flex h-[34rem] w-full max-w-2xl flex-col overflow-hidden rounded-2xl border border-[var(--line-soft)] bg-[var(--surface-1)] text-left">
<header className="flex items-center justify-between gap-3 border-b border-[var(--line-soft)] px-4 py-3">
<div className="flex flex-col">
<Heading level={2} size="sm">Q3 revenue review</Heading>
<Text size="sm" variant="muted">Glin assistant, 3 sources</Text>
</div>
<Badge tone="success" variant="soft">Online</Badge>
</header>
<MessageScroller label="Conversation" className="min-h-0 flex-1">
<Message role="user">
<MessageAvatar fallback="MK" alt="" />
<MessageContent>
<div className="flex flex-col gap-2">
<span>Summarize how Q3 went compared with Q2.</span>
<div className="flex flex-wrap gap-2">
<Attachment name="q3-revenue.csv" size={48210} />
<Attachment name="board-notes.pdf" size={1203400} />
</div>
</div>
</MessageContent>
</Message>
<Message role="assistant">
<MessageAvatar fallback="G" alt="" />
<MessageContent
actions={
<MessageActions>
<MessageCopyAction value={reply} />
<MessageAction label="Regenerate"><ArrowsClockwise /></MessageAction>
<MessageAction label="Good response" pressed><ThumbsUp /></MessageAction>
<MessageAction label="Bad response"><ThumbsDown /></MessageAction>
</MessageActions>
}
>
<div className="flex flex-col gap-2">
<Thinking active={false} doneLabel="Thought for 4 seconds">Compared monthly totals, then split growth into new and expansion revenue.</Thinking>
<span>{reply}</span>
</div>
</MessageContent>
</Message>
<Message role="user">
<MessageAvatar fallback="MK" alt="" />
<MessageContent>Which regions grew fastest?</MessageContent>
</Message>
<Message role="assistant">
<MessageAvatar fallback="G" alt="" />
<MessageContent>
<StreamingText text="Europe led with 19% growth, followed by North America at 11%. APAC was flat after the pricing change in August." speed={30} />
</MessageContent>
</Message>
<Message role="assistant">
<MessageAvatar fallback="G" alt="" />
<MessageContent><Thinking /></MessageContent>
</Message>
</MessageScroller>
<div className="border-t border-[var(--line-soft)] p-3">
<PromptInput
placeholder="Ask a follow up"
maxLength={500}
onSubmit={(value) => console.log(value)}
actions={<MessageAction label="Attach file"><Paperclip /></MessageAction>}
/>
</div>
</div>
)
}

Variants

Every vocabulary variant on the default tone. Omit variant to follow the ambient design style.

glinr.pdf23 KB
solid.pdf23 KB
plain.pdf23 KB
soft.pdf23 KB
outline.pdf23 KB
ghost.pdf23 KB
gradient.pdf23 KB
import { Attachment } from "@glinui/ui"
export function AttachmentVariantsDemo() {
return (
<div className="flex flex-wrap items-center gap-3">
<Attachment variant="glinr" name="glinr.pdf" size={24000} />
<Attachment variant="solid" name="solid.pdf" size={24000} />
<Attachment variant="plain" name="plain.pdf" size={24000} />
<Attachment variant="soft" name="soft.pdf" size={24000} />
<Attachment variant="outline" name="outline.pdf" size={24000} />
<Attachment variant="ghost" name="ghost.pdf" size={24000} />
<Attachment variant="gradient" name="gradient.pdf" size={24000} />
</div>
)
}

Glass (opt-in)

Glass is opt-in and needs a backdrop behind it. Switch the preview stage to Vivid or Photo to see the frosted surface.

glass.pdf23 KB
import { Attachment } from "@glinui/ui"
export function AttachmentGlassDemo() {
return (
<Attachment variant="glass" name="glass.pdf" size={24000} />
)
}

Accessibility

  • Upload progress is exposed as role="progressbar" with aria-valuenow.
  • The remove button is named "Remove <file name>".
  • Thumbnail images use the file name as alt text; chip icons are decorative.

Keyboard Interactions

KeyAction
Enter / SpaceActivate the remove button.

ARIA Attributes

  • `role="progressbar"` with `aria-valuenow`
  • `aria-label` on the remove button

Reduced Motion

Progress width changes animate only when motion is allowed.

Affected properties

widthbackground-color

API Reference

PropTypeRequiredDefaultDescription
namestringYes-File name. Required.
sizenumberNo-Size in bytes, formatted automatically.
typestringNo-MIME type, used with the extension to choose the icon.
progressnumberNo-0 to 100. Below 100 shows a progress bar.
previewUrlstringNo-Image source for thumbnails.
display"chip" | "thumbnail"NochipRow chip or square image tile.
variantSurfaceVariant | "default" | "raised" | "frosted"Noambient (glinr)Surface variant. Omit to follow the ambient design style (glinr by default, plain for minimal, glass for glass).
toneSurfaceToneNoneutralColour tone for the vocabulary variants.
onRemove() => voidNo-Renders a remove button labelled with the file name.
removeLabelstringNo-Auto-generated from TypeScript source.

Source

Import directly from the package or browse the source on GitHub. Click any file to view it.

Import

import { Attachment } from "@glinui/ui"

Source Files

Dependencies

@glinui/ui@phosphor-icons/reactclass-variance-authority

Generated API Snapshot

Beta

Auto-extracted from TypeScript source in packages/ui/src/components/attachment.tsx. This section is in beta and may lag behind hand-curated docs. Regenerate with pnpm --filter @glinui/docs api:generate.

Generated: 2026-10-08T01:41:21.546Z · Full index: /docs/api-metadata

Primary Props Type

AttachmentProps

AttachmentProps

PropTypeRequiredDefaultDescription
display"chip" | "thumbnail"No"chip"Variant option from attachmentDisplay.
namestringYes--
onRemove() => voidNo--
previewUrlstringNo-Image URL. With `display="thumbnail"` it fills the tile.
progressnumberNo-0 to 100. When defined and below 100 a progress bar is shown.
removeLabelstringNo--
sizenumberNo-Size in bytes.
toneSurfaceToneNo--
typestringNo-MIME type or extension used to pick the icon.
variantSurfaceVariant | "default" | "raised" | "frosted"No-Visual variant. Omit for the ambient design style (glinr by default). Vocabulary: glinr, solid, plain, soft, outline, ghost, gradient, glass.