Skip to content
weftGitHub

PresenceStack

Who is here now: a row of avatars in each person's colour.

SourcePackage @weft-js/warp

Preview

tsx
import { PresenceStack } from '@weft-js/warp'import type { Peer } from '@weft-js/warp'export function Overflow() {  return (    <PresenceStack peers={PEERS} max={3} />  )}const PEOPLE = [  { id: 'mara', name: 'Mara Lindqvist', email: 'mara@weft.dev', role: 'Owner', team: 'Platform', initials: 'ML' },  { id: 'jun', name: 'Jun Tanaka', email: 'jun@weft.dev', role: 'Admin', team: 'Platform', initials: 'JT' },  { id: 'priya', name: 'Priya Raman', email: 'priya@weft.dev', role: 'Member', team: 'Web', initials: 'PR' },  { id: 'tomas', name: 'Tomás Oliveira', email: 'tomas@weft.dev', role: 'Member', team: 'Web', initials: 'TO' },  { id: 'lena', name: 'Lena Fischer', email: 'lena@weft.dev', role: 'Member', team: 'Data', initials: 'LF' },  { id: 'noor', name: 'Noor Haddad', email: 'noor@weft.dev', role: 'Viewer', team: 'Data', initials: 'NH' },  { id: 'sam', name: 'Sam Okafor', email: 'sam@weft.dev', role: 'Member', team: 'Mobile', initials: 'SO' },  { id: 'ines', name: 'Inés Vidal', email: 'ines@weft.dev', role: 'Viewer', team: 'Mobile', initials: 'IV' },]const PEERS: Peer[] = PEOPLE.map((p, i) => ({ id: p.id, name: p.name, color: i + 1 }))

Installation

npm install @weft-js/warp

Usage

tsx
import { PresenceStack } from '@weft-js/warp'<PresenceStack peers={PEERS} max={3} />

People coming and going

Joins and leaves are announced as one summary.

tsx
import { state } from '@weft-js/core'import { Button, PresenceStack, Row, Stack } from '@weft-js/warp'import type { Peer } from '@weft-js/warp'export function Room() {  const peers = state(PEERS.slice(0, 3))  const join = () => (peers.value = PEERS.slice(0, peers.value.length + 1))  const leave = () => (peers.value = peers.value.slice(0, -1))  return (    <Stack gap="related" align="center">      <PresenceStack peers={peers} max={4} />      <Row gap="tight">        <Button size="sm" emphasis="outline" tone="neutral" aria-disabled={peers.value.length === PEERS.length ? 'true' : undefined} onClick={join}>Someone joins</Button>        <Button size="sm" emphasis="ghost" tone="neutral" aria-disabled={peers.value.length < 2 ? 'true' : undefined} onClick={leave}>Someone leaves</Button>      </Row>    </Stack>  )}const PEOPLE = [  { id: 'mara', name: 'Mara Lindqvist', email: 'mara@weft.dev', role: 'Owner', team: 'Platform', initials: 'ML' },  { id: 'jun', name: 'Jun Tanaka', email: 'jun@weft.dev', role: 'Admin', team: 'Platform', initials: 'JT' },  { id: 'priya', name: 'Priya Raman', email: 'priya@weft.dev', role: 'Member', team: 'Web', initials: 'PR' },  { id: 'tomas', name: 'Tomás Oliveira', email: 'tomas@weft.dev', role: 'Member', team: 'Web', initials: 'TO' },  { id: 'lena', name: 'Lena Fischer', email: 'lena@weft.dev', role: 'Member', team: 'Data', initials: 'LF' },  { id: 'noor', name: 'Noor Haddad', email: 'noor@weft.dev', role: 'Viewer', team: 'Data', initials: 'NH' },  { id: 'sam', name: 'Sam Okafor', email: 'sam@weft.dev', role: 'Member', team: 'Mobile', initials: 'SO' },  { id: 'ines', name: 'Inés Vidal', email: 'ines@weft.dev', role: 'Viewer', team: 'Mobile', initials: 'IV' },]const PEERS: Peer[] = PEOPLE.map((p, i) => ({ id: p.id, name: p.name, color: i + 1 }))

Accessibility

  • This has no shortcuts of its own: controls inside it use Tab, Enter and Space as usual, and warp's parts keep their roles and labels.

API Reference

PropTypeDefaultDescription
peersLive<Peer[]>—Who is here.
maxnumber4How many avatars before +n.
followingstring | null—The id of the person being followed.
labelstring'People here'Names the group.
EventTypeDefaultDescription
onFollow((p: Peer | null) => void)—Turns each avatar into a follow toggle.

Parts

Every element carries a stable name, so you can style it from your own CSS: [data-scope="avatar-stack"] [data-part="sr-only"]

sr-onlymorecontent

RelatedFollowingCursorsSection peers