Large Attachment

Checks attachments against each service's real size limit, counting email's Base64 overhead, so a message doesn't bounce for being too big.

StopsEmail bounced for a big attachment

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/large-attachment.json

The files land in your project. They're yours to edit.

Usage

large-attachment-usage.tsx
import { LargeAttachment, estimateCompressed, formatSize, type AttachmentFile, } from "@/components/ui/large-attachment"

<LargeAttachment
  files={files}
  onSendAsLink={(over) => {
    setLinked((prev) => [...prev, ...over])
    setFiles((prev) => prev.filter((file) => !over.includes(file)))
  }}
  onCompress={() => setFiles((prev) => prev.map((file) => ({ ...file, size: estimateCompressed(file) })))}
/>

What it catches

  • Total over the service limit once Base64 encoding is counted
  • The fewest (largest-first) files to remove so the rest fits
  • Size after sending culprits as a link
  • Compression that won't help already-compressed files

Props

LargeAttachment props
PropTypeDefault
files* (required)

Files currently attached.

AttachmentFile[]—
limits

Services to check against. The selected one drives the fixes.

SendLimit[]DEFAULT_LIMITS
value

Controlled id of the service you are sending through.

string—
defaultValue

Initial service id when uncontrolled.

string—
onValueChange

Called with the id of the service the user picks.

(id: string) => void—
onSendAsLink

Upload these files and send a link instead.

(files: AttachmentFile[]) => void—
onCompress

Compress the attachments. Receives the estimated result size.

(estimatedBytes: number) => void—
estimate

Override the compression estimate per file.

(file: AttachmentFile) => numberestimateCompressed
overText

Templates: `{size}`, `{limit}`, `{over}`, `{raw}`, `{count}` are filled in.

string"{size} won't send: {over} over the {limit} limit."
encodedText

Explains the Base64 overhead.

string"Your files are {raw}, but email encoding adds about 37%."
fitsText

Shown when everything fits.

string"{size} fits the {limit} limit."
culpritLabel

Label marking files that push it over.

string"Pushes it over"
linkLabel

Button text to send culprits as a link.

string"Send {count} as a link"
compressLabel

Button text to compress.

string"Compress"
noGainText

Shown when compressing won't help.

string"already compressed"
fitsLabel

Badge for a service the message fits.

string"fits"
tooBigLabel

Badge for a service the message is too big for.

string"still too big"
groupLabel

Accessible label of the service picker.

string"Send through"

* required. Also accepts the native props of <div> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Service picker is a labelled radiogroup with roving tabindex and arrow keys
  • Status is a polite live region
  • Attachment list is a labelled list; icons are aria-hidden

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

  • Built-in limits (Gmail 25 MB, Outlook.com 20 MB, Discord free 10 MB) can change and differ for workplace accounts; pass your own
  • The recipient's server may have a lower limit than the sender's
  • Compression savings are estimates

What it stops

Email bounced for a big attachment

Also known as

attachments · file size limit · gmail · upload · compress · drive link · base64 · email

Field type

Dependencies

Pairs well with