# Share Dialog

> The Share dialog of a collaborative app: invite people by email with a role, manage who has access, set link access and copy the link.

- Type: React block (Overlays)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add share-dialog`
- Docs: https://wingo-ui.com/components/share-dialog

## Interaction

Share opens the dialog with the cursor in the email field (a bottom sheet below 768px). Type or paste addresses: Enter, a comma or a space turns each into a chip, Backspace brings the last one back as text, and people from suggestions show their name and photo. A bad address or someone who already has access stays as a red chip with the reason under the field. Pick the role, add an optional message (mod+Enter sends) and press Invite: the button loads, then the new people slide into the list as Pending with a Resend link and a toast confirms. A failed invite keeps the chips and shows the error inline. In the list change anyone's role from their picker, or open the ... menu to resend, make them the owner or remove access after a confirmation; the owner's row is fixed. Under General access switch between Restricted, Anyone at the domain and Anyone with the link, and pick the link role. Copy link swaps its label to Link copied and shows a toast; Done or Escape closes. Read-only viewers see the list and copy the link but change nothing.

## Installation

```bash
npx wingo-ui@latest add share-dialog
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/share-dialog` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/blocks/share-dialog.tsx`
- `components/blocks/share-dialog-data.ts`

npm dependencies: `npm i motion lucide-react`

Wingo UI dependencies (installed automatically):

- [Alert Dialog](https://wingo-ui.com/components/alert-dialog) ([Markdown](https://wingo-ui.com/components/alert-dialog.md))
- [Avatar](https://wingo-ui.com/components/avatar) ([Markdown](https://wingo-ui.com/components/avatar.md))
- [Badge](https://wingo-ui.com/components/badge) ([Markdown](https://wingo-ui.com/components/badge.md))
- [Button](https://wingo-ui.com/components/button) ([Markdown](https://wingo-ui.com/components/button.md))
- [Copy Button](https://wingo-ui.com/components/copy-button) ([Markdown](https://wingo-ui.com/components/copy-button.md))
- [Dialog](https://wingo-ui.com/components/dialog) ([Markdown](https://wingo-ui.com/components/dialog.md))
- [Dropdown Menu](https://wingo-ui.com/components/dropdown-menu) ([Markdown](https://wingo-ui.com/components/dropdown-menu.md))
- [Email Input](https://wingo-ui.com/components/email-input) ([Markdown](https://wingo-ui.com/components/email-input.md))
- [Select](https://wingo-ui.com/components/select) ([Markdown](https://wingo-ui.com/components/select.md))
- [Textarea](https://wingo-ui.com/components/textarea) ([Markdown](https://wingo-ui.com/components/textarea.md))
- [Toast](https://wingo-ui.com/components/toast) ([Markdown](https://wingo-ui.com/components/toast.md))
- [useControllableState](https://wingo-ui.com/components/use-controllable-state) ([Markdown](https://wingo-ui.com/components/use-controllable-state.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
"use client"

import { ShareDialog } from "@/components/blocks/share-dialog"
import type { ShareDialogPerson } from "@/components/blocks/share-dialog-data"

export function ShareDoc({ docId, people }: { docId: string; people: ShareDialogPerson[] }) {
  return (
    <ShareDialog
      resourceName="Q4 Launch Plan"
      link={`https://docs.acme.com/d/${docId}`}
      defaultPeople={people}
      onInvite={async (emails, role, message) => {
        const res = await fetch(`/api/docs/${docId}/invites`, { method: "POST", body: JSON.stringify({ emails, role, message }) })
        if (!res.ok) throw new Error("The invites were not sent. Try again.")
      }}
      onRoleChange={(personId, role) =>
        fetch(`/api/docs/${docId}/members/${personId}`, { method: "PATCH", body: JSON.stringify({ role }) })
      }
      onRemove={(personId) => fetch(`/api/docs/${docId}/members/${personId}`, { method: "DELETE" })}
      onAccessChange={(access) => fetch(`/api/docs/${docId}`, { method: "PATCH", body: JSON.stringify({ access }) })}
    />
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `resourceName?` | `string` | `"Q4 Launch Plan"` | The file, doc or project being shared; it fills the title (Share "Q4 Launch Plan") and the remove confirmation. |
| `title?` | `ReactNode \| ((name: string) => ReactNode)` |  | The dialog title, or a function of resourceName. Default Share "name" from labels.title. |
| `description?` | `ReactNode` | `""` | A line under the title, e.g. who can see the file today. |
| `people?` | `ShareDialogPerson[]` |  | Controlled list of people with access: { id, name, email, avatar?, role, owner?, pending? }. Pair it with onPeopleChange, which fires after invites, role changes, removals and a new owner. |
| `defaultPeople?` | `ShareDialogPerson[]` | `"SHARE_DIALOG_PEOPLE"` | Starting list while uncontrolled; the sample is an owner, an editor, a commenter and a pending invite. |
| `onPeopleChange?` | `(people: ShareDialogPerson[]) => void` |  | The list after every change the dialog makes. |
| `roles?` | `ShareDialogRole[]` | `"DEFAULT_SHARE_DIALOG_ROLES"` | The role options { value, label, description? } for invites, rows and the link. Default Viewer, Commenter and Editor. |
| `suggestions?` | `ShareDialogPerson[]` | `"[]"` | People the email field suggests while typing (name, address and photo); invited ones keep their name and photo in the list. |
| `link?` | `string` | `"https://docs.acme.com/d/q4-launch-plan"` | The address Copy link puts on the clipboard. |
| `domain?` | `string` | `"acme.com"` | The organization domain for the "Anyone at acme.com" access option. Empty hides that option. |
| `labels?` | `Partial<ShareDialogLabels>` |  | Merged over DEFAULT_SHARE_DIALOG_LABELS. Every string: the trigger, title, placeholder, roles, toasts, confirmations, access options and buttons. Functions take the name or count. |
| `trigger?` | `ReactNode` |  | The element that opens the dialog, rendered with asChild. Default a soft Share button; null renders none, for a dialog driven by open. |
| `defaultInviteRole?` | `string` | `"viewer"` | The role preselected for new invites. |
| `defaultAccess?` | `"restricted" \| "domain" \| "anyone"` | `"restricted"` | Starting general access while uncontrolled: only people with access, anyone at the domain, or anyone with the link. |
| `access?` | `"restricted" \| "domain" \| "anyone"` |  | Controlled general access; pair it with onAccessChange. |
| `onAccessChange?` | `(access: ShareDialogAccess) => void` |  | A new general access was picked. |
| `defaultLinkRole?` | `string` | `"viewer"` | Starting role for people who open the link, shown while access is not restricted. |
| `linkRole?` | `string` |  | Controlled link role; pair it with onLinkRoleChange. |
| `onLinkRoleChange?` | `(role: string) => void` |  | A new link role was picked. |
| `onInvite?` | `(emails: string[], role: string, message?: string) => void \| Promise<unknown>` |  | Invite was pressed with valid, new addresses. Return a promise to show the loading state; a rejection keeps the chips and shows its message under the field. |
| `onRoleChange?` | `(personId: string, role: string) => void \| Promise<unknown>` |  | A row's role changed. It updates at once; a rejection puts the old role back with an error toast. |
| `onRemove?` | `(personId: string) => void \| Promise<unknown>` |  | Remove access was confirmed. A rejection shows in the confirmation and keeps the row. |
| `onTransferOwnership?` | `(personId: string) => void \| Promise<unknown>` |  | Adds Make owner to each row's menu; after the confirmation that person becomes the owner and the old owner an editor. |
| `onResend?` | `(personId: string) => void \| Promise<unknown>` |  | Resend on a pending invite. |
| `showMessage?` | `boolean` | `true` | A message field slides in under the invite row once there are addresses. |
| `readOnly?` | `boolean` | `false` | For viewers: no invite row, roles and access as plain text, no row menus. Copy link still works. |
| `defaultOpen?` | `boolean` | `false` | Opens on mount while uncontrolled. |
| `open?` | `boolean` |  | Controlled open state; pair it with onOpenChange. The dialog stays open while an invite is sending. |
| `onOpenChange?` | `(open: boolean) => void` |  | Done, the close button, Escape, an outside click or the sheet drag. |
| `size?` | `"md" \| "lg"` | `"md"` | Dialog width: md 520px, lg 720px. Below 768px it is a bottom sheet either way. |
| `responsive?` | `boolean` | `true` | A bottom sheet below 768px, with the role pickers and row menus as sheets too. |
| `className?` | `string` |  | Classes for the dialog content. Other props go to the dialog content too (onOpenAutoFocus, aria-*). |
| `classNames?` | `Partial<Record<ShareDialogSlot, string>>` |  | Classes for overlay, content, header, body, footer, invite, people, person, access, copy and done. |

## Parts

### DEFAULT_SHARE_DIALOG_LABELS (constant)

The English strings; spread it and override what you need for another language.

### DEFAULT_SHARE_DIALOG_ROLES (constant)

Viewer, Commenter and Editor with a short description each.

### SHARE_DIALOG_PEOPLE (constant)

Sample people with access (share-dialog-data.ts): an owner, an editor, a commenter and a pending invite.

### SHARE_DIALOG_SUGGESTIONS (constant)

Sample teammates for the suggestions prop.

### ShareDialogPerson (type)

{ id, name, email, avatar?, role, owner?, pending? }: one row of People with access.

### ShareDialogRole (type)

{ value, label, description? }: one role option.

### ShareDialogAccess (type)

"restricted" | "domain" | "anyone".

### ShareDialogLabels (type)

The shape of labels.

## Examples

- **Shared with four people**: An owner, an editor, a commenter and a pending invite; suggestions, Make owner and Remove access with a confirmation.
- **Anyone with the link**: defaultAccess="anyone" with commenters by link, opened from a custom trigger.
- **Invite error**: The first invite fails: the chips stay and the message shows under the field. Try again and it goes through.
- **Read-only viewer**: readOnly: the list and the access as text, Copy link still works.
- **Large, no message**: size="lg" with a description, editors by default and showMessage={false}.

## See also

- [Members table](https://wingo-ui.com/components/members-table) ([Markdown](https://wingo-ui.com/components/members-table.md))
- [Permissions Matrix](https://wingo-ui.com/components/permissions-matrix) ([Markdown](https://wingo-ui.com/components/permissions-matrix.md))
- [Users Admin Page](https://wingo-ui.com/components/users-admin-page) ([Markdown](https://wingo-ui.com/components/users-admin-page.md))
- [Dialog](https://wingo-ui.com/components/dialog) ([Markdown](https://wingo-ui.com/components/dialog.md))
- [Tag Input](https://wingo-ui.com/components/tag-input) ([Markdown](https://wingo-ui.com/components/tag-input.md))
- [Copy Button](https://wingo-ui.com/components/copy-button) ([Markdown](https://wingo-ui.com/components/copy-button.md))

---

Canonical: https://wingo-ui.com/components/share-dialog
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
