KQ-1342 · MAIL/CLIENT

Mail Client

A complete three-pane mail screen built around mail-inbox and bin-lid: mailboxes whose current-folder pill travels on snap, the inbox list with its swipe triage, and a reading pane where a thread arrives from 4px on glide, earlier messages open on a measured glide, a quick reply grows on glide and lands on snap, Archive and Snooze leave an Undo toast that lands on recoil, and Delete drops the thread in the bin until its undo ring drains. `folders` keeps the mailboxes as a pane, an icon rail or a drawer that slides over the list on glide; below 640px the panes become a stack whose pushes slide in on glide over a dimming, shifting screen, and a swipe from a pushed screen's left edge follows the finger 1:1 and goes back when the projected throw passes halfway. Back, Alt+← and Escape go back, / searches, c composes, and the list keeps its own j, k, x, e, b, s, u and z.

KQ-1342 · MAIL/CLIENT
Mail
Noor Halvorsennoor@fieldline.example

Labels

6.2 GB of 15.0 GB used

Inbox

4 unread

j and k move between threads, x selects, Enter opens, e archives, b snoozes, s stars, u marks unread, z undoes. Swipe a row right or left for its actions.

No message open

Pick a thread to read it here. j and k move, Enter opens.

4 unread · swipe a row to triage

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/mail-client

Props

density
"compact" | "cozy" | "roomy"
default "cozy"
Row height and type size in the list, and the reading pane's type.
preview
number
default 1
Lines of message preview under each subject in the list, 0 to 2.
folders
"pane" | "rail" | "drawer"
default "pane"
How the mailboxes live beside the list on wide screens: a labelled pane, a 60px icon rail with badges, or a drawer behind the menu button. A phone always stacks them.
threads / defaultThreads
MailThread[]
default defaultMailClientThreads
Every thread in every folder (mail-inbox's type). Controlled or uncontrolled.
onThreadsChange
(threads: MailThread[]) => void
default —
Fires from the swipe, key, button, reply or delete that changed the threads, with all of them.
folder / defaultFolder
"inbox" | "snoozed" | "archive"
default "inbox"
The folder on screen. Controlled or uncontrolled.
onFolderChange
(folder: MailFolder) => void
default —
A mailbox was chosen.
open / defaultOpen
string | null
default null
The thread in the reading pane. Controlled or uncontrolled.
onOpenChange
(id: string | null) => void
default —
Fires from the row, the arrows, the back swipe or the action that opened or closed a thread.
account
MailPerson
default defaultMailClientAccount
Whose mail it is: shown at the head of the mailboxes, and the sender of replies.
storage
{ used: number; total: number }
default { used: 6.2e9, total: 15e9 }
The mailbox's storage in bytes, for the meter under the mailboxes.
now
Date | number
default defaultMailClientNow
The moment times are read against, so server and browser agree.
zoneOffset
number
default 0
Minutes east of UTC that times are shown in.
onSend
(draft: MailDraft) => void
default —
A reply (with its threadId) or a new message from Compose was sent.
onArchive
(ids: string[]) => void
default —
Threads sent to the archive, from the list or the reading pane.
onSnooze
(ids: string[], until: number) => void
default —
Threads snoozed, and until when (epoch ms).
onDelete
(ids: string[]) => void
default —
Threads deleted, once the bin's undo window ran out.
onStarChange
(id: string, starred: boolean) => void
default —
A thread was starred or unstarred.
onReadChange
(ids: string[], unread: boolean) => void
default —
Threads marked read by opening them, or unread from the list or the pane.
status
"ready" | "loading" | "error"
default "ready"
Loading draws placeholder rows; error offers Retry.
onRetry
() => void
default —
Retry was pressed after the mail failed to load.
label
string
default "Mail"
The screen's accessible name.
sound
boolean
default false
Play the slides, stars and sends, only for the visitor's own actions; the list and the bin play theirs too.
disabled
boolean
default false
Read only: nothing can be moved, sent or deleted.
className
string
default —
Classes for the root. It is 560px tall by default; pass a height class to change it.