# Invitations

Collect several email addresses, resolve invalid entries, and send invitations together.

Import: @slices.sh/auth/headless

Public parts: 37

## Installation

### 1. Install the service registry

services/index.ts, which `import "@/services"` loads. A provider setup writes the same file; skip this if your app has one.

```bash
npx shadcn@latest add "https://slices.sh/r/services.json"
```

### 2. Install the headless library

```bash
npm install @slices.sh/auth@^0.1.10
```

### 3. Install the service

Install the provider configuration via shadcn or the slices CLI. Skip this step if your app already has the service configured. Provider commands and provisioning: https://slices.sh/docs/services/auth.md

## Anatomy

### Composition

```text
User.Root
  Organization.Active
    Invite.Form
      Invite.Label
      Invite.Email
      Invite.Role
        Invite.RoleOption
      Invite.Submit
      Members.InvitationCount
      Invite.Error
    Invite.Batch.Form
      Invite.Batch.Items
        Invite.Batch.Role
        Invite.Batch.Remove
        Invite.Batch.Problem
        Invite.Batch.Summary
        Invite.Batch.CopyLink
      Invite.Batch.Label
      Invite.Batch.Input
      Invite.Batch.Hint
      Invite.Batch.Add
      Invite.Batch.List
      Invite.Batch.Expiry
      Invite.Batch.Submit
      Invite.Batch.Error
    Members.Invitation
      Members.InvitationEmail
      Members.InvitationExpiry
      Members.CopyInvitation
      Members.RenewInvitation
      Members.CancelInvitation
      Members.CancelInvitationError
      Members.RenewInvitationError
  Invite.Sent
    Invite.CopyLink
  Invite.NotAllowed
  Members.InvitationsLoading
  Members.InvitationsEmpty
  Members.InvitationsError
```

## Minimal composition

```tsx
"use client"

import "@/services"
import { User, Invite } from "@slices.sh/auth/headless"

export function Example() {
  return (
    <User.Root>
      <Invite.Batch.Form>
        <Invite.Batch.Label>Email addresses</Invite.Batch.Label>
        <Invite.Batch.Input />
        <Invite.Batch.Add>Add addresses</Invite.Batch.Add>
        <Invite.Batch.List>
          <Invite.Batch.Items>
            <Invite.Batch.Remove>Remove</Invite.Batch.Remove>
          </Invite.Batch.Items>
        </Invite.Batch.List>
        <Invite.Batch.Submit>Send invitations</Invite.Batch.Submit>
        <Invite.Batch.Summary />
        <Invite.Batch.Error />
      </Invite.Batch.Form>
    </User.Root>
  )
}

```

Requires an authenticated account. Organization controls also require organizations and the appropriate role.

## Invite a member

```tsx
"use client"

import "@/services"
import { User, Invite } from "@slices.sh/auth/headless"

export function Example() {
  return (
    <User.Root>
      <Invite.Form>
        <Invite.Label field="email">Email</Invite.Label>
        <Invite.Email />
        <Invite.Submit>Send invitation</Invite.Submit>
        <Invite.Sent>Invitation sent.</Invite.Sent>
        <Invite.Error />
      </Invite.Form>
    </User.Root>
  )
}

```

Requires an authenticated account. Organization controls also require organizations and the appropriate role.

## Complete API reference

### Members.CancelInvitationError

content · Element: p

Invitation is an <li> repeated once per pending invitation with { role }; InvitationEmail is its address, InvitationCount the total, InvitationExpiry a <time> with { expiresAt, expired }. CancelInvitation and RenewInvitation act on it, each with its error part, and render nothing when you cannot manage invitations. CopyInvitation copies its link, with { copied, error }. InvitationsLoading, InvitationsEmpty and InvitationsError cover the read.

Import: @slices.sh/auth/headless

Members.CancelInvitationError requires an invitation row.

```tsx
PartProps<"p", { code: AuthErrorCode; message: string }>
```

Alternate import: Members.CancelInvitationError from @slices.sh/auth/headless/members

Source: @slices.sh/auth/src/headless/members/cancel-invitation-error.tsx

### Members.RenewInvitationError

content · Element: p

Invitation is an <li> repeated once per pending invitation with { role }; InvitationEmail is its address, InvitationCount the total, InvitationExpiry a <time> with { expiresAt, expired }. CancelInvitation and RenewInvitation act on it, each with its error part, and render nothing when you cannot manage invitations. CopyInvitation copies its link, with { copied, error }. InvitationsLoading, InvitationsEmpty and InvitationsError cover the read.

Import: @slices.sh/auth/headless

Members.RenewInvitationError requires an invitation row.

```tsx
PartProps<"p", { code: AuthErrorCode; message: string }>
```

Alternate import: Members.RenewInvitationError from @slices.sh/auth/headless/members

Source: @slices.sh/auth/src/headless/members/renew-invitation-error.tsx

### Members.RenewInvitation

action · Element: button

Invitation is an <li> repeated once per pending invitation with { role }; InvitationEmail is its address, InvitationCount the total, InvitationExpiry a <time> with { expiresAt, expired }. CancelInvitation and RenewInvitation act on it, each with its error part, and render nothing when you cannot manage invitations. CopyInvitation copies its link, with { copied, error }. InvitationsLoading, InvitationsEmpty and InvitationsError cover the read.

Import: @slices.sh/auth/headless

Members.RenewInvitation requires an invitation row.

```tsx
PartProps<"button", { pending: boolean; retryAfter: number }>
```

Alternate import: Members.RenewInvitation from @slices.sh/auth/headless/members

Source: @slices.sh/auth/src/headless/members/renew-invitation.tsx

### Members.InvitationExpiry

content · Element: time

Invitation is an <li> repeated once per pending invitation with { role }; InvitationEmail is its address, InvitationCount the total, InvitationExpiry a <time> with { expiresAt, expired }. CancelInvitation and RenewInvitation act on it, each with its error part, and render nothing when you cannot manage invitations. CopyInvitation copies its link, with { copied, error }. InvitationsLoading, InvitationsEmpty and InvitationsError cover the read.

Import: @slices.sh/auth/headless

Members.InvitationExpiry requires an invitation row.

```tsx
PartProps<"time", { expiresAt: Date | null; expired: boolean }>
```

Alternate import: Members.InvitationExpiry from @slices.sh/auth/headless/members

Source: @slices.sh/auth/src/headless/members/invitation-expiry.tsx

### Members.InvitationsError

content · Element: div

Invitation is an <li> repeated once per pending invitation with { role }; InvitationEmail is its address, InvitationCount the total, InvitationExpiry a <time> with { expiresAt, expired }. CancelInvitation and RenewInvitation act on it, each with its error part, and render nothing when you cannot manage invitations. CopyInvitation copies its link, with { copied, error }. InvitationsLoading, InvitationsEmpty and InvitationsError cover the read.

Import: @slices.sh/auth/headless

```tsx
PartProps<"div", { code: AuthErrorCode; message: string }>
```

Alternate import: Members.InvitationsError from @slices.sh/auth/headless/members

Source: @slices.sh/auth/src/headless/members/invitations-error.tsx

### Members.InvitationsEmpty

gate

Invitation is an <li> repeated once per pending invitation with { role }; InvitationEmail is its address, InvitationCount the total, InvitationExpiry a <time> with { expiresAt, expired }. CancelInvitation and RenewInvitation act on it, each with its error part, and render nothing when you cannot manage invitations. CopyInvitation copies its link, with { copied, error }. InvitationsLoading, InvitationsEmpty and InvitationsError cover the read.

Import: @slices.sh/auth/headless

```tsx
GateProps
```

Alternate import: Members.InvitationsEmpty from @slices.sh/auth/headless/members

Source: @slices.sh/auth/src/headless/members/invitations-empty.tsx

### Members.InvitationsLoading

gate

Invitation is an <li> repeated once per pending invitation with { role }; InvitationEmail is its address, InvitationCount the total, InvitationExpiry a <time> with { expiresAt, expired }. CancelInvitation and RenewInvitation act on it, each with its error part, and render nothing when you cannot manage invitations. CopyInvitation copies its link, with { copied, error }. InvitationsLoading, InvitationsEmpty and InvitationsError cover the read.

Import: @slices.sh/auth/headless

```tsx
GateProps
```

Alternate import: Members.InvitationsLoading from @slices.sh/auth/headless/members

Source: @slices.sh/auth/src/headless/members/invitations-loading.tsx

### Members.InvitationCount

content · Element: span

Invitation is an <li> repeated once per pending invitation with { role }; InvitationEmail is its address, InvitationCount the total, InvitationExpiry a <time> with { expiresAt, expired }. CancelInvitation and RenewInvitation act on it, each with its error part, and render nothing when you cannot manage invitations. CopyInvitation copies its link, with { copied, error }. InvitationsLoading, InvitationsEmpty and InvitationsError cover the read.

Import: @slices.sh/auth/headless

```tsx
PartProps<"span", { count: number }>
```

Alternate import: Members.InvitationCount from @slices.sh/auth/headless/members

Source: @slices.sh/auth/src/headless/members/invitation-count.tsx

### Members.CopyInvitation

action · Element: button

Invitation is an <li> repeated once per pending invitation with { role }; InvitationEmail is its address, InvitationCount the total, InvitationExpiry a <time> with { expiresAt, expired }. CancelInvitation and RenewInvitation act on it, each with its error part, and render nothing when you cannot manage invitations. CopyInvitation copies its link, with { copied, error }. InvitationsLoading, InvitationsEmpty and InvitationsError cover the read.

Import: @slices.sh/auth/headless

Members.CopyInvitation must be inside <Members.Invitation>.

```tsx
PartProps<"button", { copied: boolean; error: string | null }>
```

Alternate import: Members.CopyInvitation from @slices.sh/auth/headless/members

Source: @slices.sh/auth/src/headless/members/copy-invitation.tsx

### Members.Invitation

content · Element: li

Invitation is an <li> repeated once per pending invitation with { role }; InvitationEmail is its address, InvitationCount the total, InvitationExpiry a <time> with { expiresAt, expired }. CancelInvitation and RenewInvitation act on it, each with its error part, and render nothing when you cannot manage invitations. CopyInvitation copies its link, with { copied, error }. InvitationsLoading, InvitationsEmpty and InvitationsError cover the read.

Import: @slices.sh/auth/headless

Members.Invitation renders its items itself.

```tsx
InvitationProps

type InvitationProps = PartProps<"li", { role: Role; email: string }>
```

Alternate import: Members.Invitation from @slices.sh/auth/headless/members

Source: @slices.sh/auth/src/headless/members/invitation.tsx

### Members.InvitationEmail

content

Invitation is an <li> repeated once per pending invitation with { role }; InvitationEmail is its address, InvitationCount the total, InvitationExpiry a <time> with { expiresAt, expired }. CancelInvitation and RenewInvitation act on it, each with its error part, and render nothing when you cannot manage invitations. CopyInvitation copies its link, with { copied, error }. InvitationsLoading, InvitationsEmpty and InvitationsError cover the read.

Import: @slices.sh/auth/headless

Members.InvitationEmail must be inside <Members.Invitation>.

```tsx
PartProps<null>
```

Alternate import: Members.InvitationEmail from @slices.sh/auth/headless/members

Source: @slices.sh/auth/src/headless/members/invitation-email.tsx

### Members.CancelInvitation

action · Element: button

Invitation is an <li> repeated once per pending invitation with { role }; InvitationEmail is its address, InvitationCount the total, InvitationExpiry a <time> with { expiresAt, expired }. CancelInvitation and RenewInvitation act on it, each with its error part, and render nothing when you cannot manage invitations. CopyInvitation copies its link, with { copied, error }. InvitationsLoading, InvitationsEmpty and InvitationsError cover the read.

Import: @slices.sh/auth/headless

Members.CancelInvitation must be inside <Members.Invitation>.

Members.CancelInvitation requires an invitation row.

```tsx
PartProps<"button", { pending: boolean }>
```

Alternate import: Members.CancelInvitation from @slices.sh/auth/headless/members

Source: @slices.sh/auth/src/headless/members/cancel-invitation.tsx

### Invite.CopyLink

action · Element: button

CopyLink copies the invite link and is its own live region, with { copied, error }. Batch is the many-addresses form, documented below.

Import: @slices.sh/auth/headless

Invite.CopyLink needs Invite.Form.

```tsx
PartProps<"button", { copied: boolean; error: string | null }>
```

Alternate import: Invite.CopyLink from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/copy-link.tsx

### Invite.Form

action · Element: form

Form holds the address and the role with { pending }. Label takes field="email" | "role". Email is <input type="email" autocomplete="off">, invalid when the address is already a member's. Role is a native <select> starting at member, RoleOption one option per role. Submit is disabled until the address is valid.

Import: @slices.sh/auth/headless

Capability: organizations

```tsx
PartProps<"form", { pending: boolean }>
```

Alternate import: Invite.Form from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/form.tsx

### Invite.NotAllowed

gate

Why there is no invitation form here.

The mirror of `Invite.Form`, and not a courtesy: a plain member used to get the whole
form and, on submitting it, "You don't have permission to do that" — a sentence about
them rather than about the organization's rules. Closed while the organization is
loading, so nobody is told they are only a member for the half-second before we know.
→ docs/rules/capability-honesty.md

Import: @slices.sh/auth/headless

Capability: organizations

```tsx
GateProps
```

Alternate import: Invite.NotAllowed from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/not-allowed.tsx

### Invite.Label

content · Element: label

Form holds the address and the role with { pending }. Label takes field="email" | "role". Email is <input type="email" autocomplete="off">, invalid when the address is already a member's. Role is a native <select> starting at member, RoleOption one option per role. Submit is disabled until the address is valid.

Import: @slices.sh/auth/headless

Capability: organizations

Invite.Label must be inside <Invite.Form>.

```tsx
PartProps<"label"> & { field: keyof typeof FIELDS }
```

Alternate import: Invite.Label from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/label.tsx

### Invite.Email

input · Element: input

Form holds the address and the role with { pending }. Label takes field="email" | "role". Email is <input type="email" autocomplete="off">, invalid when the address is already a member's. Role is a native <select> starting at member, RoleOption one option per role. Submit is disabled until the address is valid.

Import: @slices.sh/auth/headless

Capability: organizations

Invite.Email must be inside <Invite.Form>.

```tsx
PartProps<"input", { invalid: boolean }>
```

Alternate import: Invite.Email from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/email.tsx

### Invite.Role

input · Element: select

Form holds the address and the role with { pending }. Label takes field="email" | "role". Email is <input type="email" autocomplete="off">, invalid when the address is already a member's. Role is a native <select> starting at member, RoleOption one option per role. Submit is disabled until the address is valid.

Import: @slices.sh/auth/headless

Capability: organizations

Invite.Role must be inside <Invite.Form>.

```tsx
PartProps<"select", { role: RoleName }>
```

Alternate import: Invite.Role from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/role.tsx

### Invite.RoleOption

content · Element: option

Form holds the address and the role with { pending }. Label takes field="email" | "role". Email is <input type="email" autocomplete="off">, invalid when the address is already a member's. Role is a native <select> starting at member, RoleOption one option per role. Submit is disabled until the address is valid.

Import: @slices.sh/auth/headless

Capability: organizations

```tsx
RoleOptionProps

type RoleOptionProps = PartProps<"option", { role: Role }>
```

Alternate import: Invite.RoleOption from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/members/role-option.tsx

### Invite.Submit

action · Element: button

Form holds the address and the role with { pending }. Label takes field="email" | "role". Email is <input type="email" autocomplete="off">, invalid when the address is already a member's. Role is a native <select> starting at member, RoleOption one option per role. Submit is disabled until the address is valid.

Import: @slices.sh/auth/headless

Capability: organizations

Invite.Submit must be inside <Invite.Form>.

```tsx
PartProps<"button", { pending: boolean }>
```

Alternate import: Invite.Submit from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/submit.tsx

### Invite.Sent

gate

Sent opens with { email } after a successful invite, until the form changes. Error is <div role="alert"> with { code, message }.

Import: @slices.sh/auth/headless

Invite.Sent must be inside <Invite.Form>.

```tsx
GateProps<{ email: string; id: string }>
```

Alternate import: Invite.Sent from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/sent.tsx

### Invite.Error

content · Element: div

Sent opens with { email } after a successful invite, until the form changes. Error is <div role="alert"> with { code, message }.

Import: @slices.sh/auth/headless

```tsx
PartProps<"div", { code: AuthErrorCode; message: string }>
```

Alternate import: Invite.Error from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/error.tsx

### Invite.Batch.Expiry

content · Element: time

CopyLink copies the organization's invite link, disabled once it expired. Expiry is a <time> with { expiresAt, expired }.

Import: @slices.sh/auth/headless

Invite.Batch.Expiry requires a recipient row.

```tsx
PartProps<"time", { expiresAt: Date | null; expired: boolean }>
```

Alternate import: Invite.Batch.Expiry from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/batch/expiry.tsx

### Invite.Batch.Form

action · Element: form

Form holds the chips with { pending }. Input takes typed or pasted addresses and turns them into chips on Enter, comma, semicolon or Tab. Label and Hint describe it. Add turns what is typed into chips.

Import: @slices.sh/auth/headless

Capability: organizations

```tsx
PartProps<"form", { pending: boolean }>
```

Alternate import: Invite.Batch.Form from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/batch/form.tsx

### Invite.Batch.Input

input · Element: input

Form holds the chips with { pending }. Input takes typed or pasted addresses and turns them into chips on Enter, comma, semicolon or Tab. Label and Hint describe it. Add turns what is typed into chips.

Import: @slices.sh/auth/headless

Capability: organizations

Invite.Batch.Input must be inside <Invite.Batch.Form>.

```tsx
PartProps<"input">
```

Alternate import: Invite.Batch.Input from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/batch/input.tsx

### Invite.Batch.Label

content · Element: label

Form holds the chips with { pending }. Input takes typed or pasted addresses and turns them into chips on Enter, comma, semicolon or Tab. Label and Hint describe it. Add turns what is typed into chips.

Import: @slices.sh/auth/headless

Capability: organizations

Invite.Batch.Label must be inside <Invite.Batch.Form>.

```tsx
PartProps<"label">
```

Alternate import: Invite.Batch.Label from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/batch/label.tsx

### Invite.Batch.Hint

content · Element: p

Form holds the chips with { pending }. Input takes typed or pasted addresses and turns them into chips on Enter, comma, semicolon or Tab. Label and Hint describe it. Add turns what is typed into chips.

Import: @slices.sh/auth/headless

Capability: organizations

Invite.Batch.Hint must be inside <Invite.Batch.Form>.

```tsx
PartProps<"p">
```

Alternate import: Invite.Batch.Hint from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/batch/hint.tsx

### Invite.Batch.Add

action · Element: button

Form holds the chips with { pending }. Input takes typed or pasted addresses and turns them into chips on Enter, comma, semicolon or Tab. Label and Hint describe it. Add turns what is typed into chips.

Import: @slices.sh/auth/headless

Capability: organizations

Invite.Batch.Add must be inside <Invite.Batch.Form>.

```tsx
PartProps<"button">
```

Alternate import: Invite.Batch.Add from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/batch/add.tsx

### Invite.Batch.List

content · Element: ul

List is the <ul>, Items repeats once per chip, Role sets that chip's role, Remove drops it, Problem gets { problem, message } for an invalid or duplicate address.

Import: @slices.sh/auth/headless

Invite.Batch.List must be inside <Invite.Batch.Form>.

```tsx
PartProps<"ul">
```

Alternate import: Invite.Batch.List from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/batch/list.tsx

### Invite.Batch.Items

content · Element: li

List is the <ul>, Items repeats once per chip, Role sets that chip's role, Remove drops it, Problem gets { problem, message } for an invalid or duplicate address.

Import: @slices.sh/auth/headless

Invite.Batch.Items must be inside <Invite.Batch.Form>.

```tsx
ItemProps

type ItemProps = PartProps<"li", { email: string; created: boolean; invalid: boolean }>
```

Alternate import: Invite.Batch.Items from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/batch/items.tsx

### Invite.Batch.Role

input · Element: select

List is the <ul>, Items repeats once per chip, Role sets that chip's role, Remove drops it, Problem gets { problem, message } for an invalid or duplicate address.

Import: @slices.sh/auth/headless

Invite.Batch.Role must be inside <Invite.Batch.Items>.

```tsx
PartProps<"select">
```

Alternate import: Invite.Batch.Role from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/batch/role.tsx

### Invite.Batch.Remove

action · Element: button

List is the <ul>, Items repeats once per chip, Role sets that chip's role, Remove drops it, Problem gets { problem, message } for an invalid or duplicate address.

Import: @slices.sh/auth/headless

Invite.Batch.Remove must be inside <Invite.Batch.Items>.

```tsx
PartProps<"button">
```

Alternate import: Invite.Batch.Remove from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/batch/remove.tsx

### Invite.Batch.Problem

content · Element: p

List is the <ul>, Items repeats once per chip, Role sets that chip's role, Remove drops it, Problem gets { problem, message } for an invalid or duplicate address.

Import: @slices.sh/auth/headless

Invite.Batch.Problem must be inside <Invite.Batch.Items>.

```tsx
PartProps<"p", { problem: EmailChipProblem | null; message: string }>
```

Alternate import: Invite.Batch.Problem from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/batch/problem.tsx

### Invite.Batch.CopyLink

action · Element: button

CopyLink copies the organization's invite link, disabled once it expired. Expiry is a <time> with { expiresAt, expired }.

Import: @slices.sh/auth/headless

Invite.Batch.CopyLink must be inside <Invite.Batch.Items>.

```tsx
PartProps<"button", { copied: boolean; error: string | null }>
```

Alternate import: Invite.Batch.CopyLink from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/batch/copy-link.tsx

### Invite.Batch.Submit

action · Element: button

Submit sends them all with { count, pending, retryAfter }. Summary gets { created, failed, pending }. Error gets { code, message }.

Import: @slices.sh/auth/headless

Invite.Batch.Submit must be inside <Invite.Batch.Form>.

```tsx
PartProps<"button", { count: number; pending: boolean; retryAfter: number }>
```

Alternate import: Invite.Batch.Submit from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/batch/submit.tsx

### Invite.Batch.Summary

content · Element: p

Submit sends them all with { count, pending, retryAfter }. Summary gets { created, failed, pending }. Error gets { code, message }.

Import: @slices.sh/auth/headless

Invite.Batch.Summary must be inside <Invite.Batch.Form>.

```tsx
PartProps<"p", { created: number; failed: number; pending: boolean }>
```

Alternate import: Invite.Batch.Summary from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/batch/summary.tsx

### Invite.Batch.Error

content · Element: div

Submit sends them all with { count, pending, retryAfter }. Summary gets { created, failed, pending }. Error gets { code, message }.

Import: @slices.sh/auth/headless

Invite.Batch.Error must be inside <Invite.Batch.Form>.

```tsx
PartProps<"div", { code: AuthErrorCode; message: string }>
```

Alternate import: Invite.Batch.Error from @slices.sh/auth/headless/invite

Source: @slices.sh/auth/src/headless/invite/batch/error.tsx

### User.Root

scope

Says which auth the User parts below read, and whether they may do without it. The parts need
no root: left out, they read the nearest auth root's config, or "default", and render nothing
without auth. `<User.Root enabled="optional">` is for the one who wants that said in the code.

Import: @slices.sh/auth/headless

```tsx
RootProps & AuthRootProps
```

Alternate import: User.Root from @slices.sh/auth/headless/user

Alternate import: User.Root from @slices.sh/auth/headless/passkeys

Alternate import: User.Root from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/root.tsx

### Organization.Active

gate

Item is a <button> repeated once per organization with { active, pending }; clicking makes it active. Personal switches to the personal account. Active renders its children with the active organization in scope, and nothing on the personal account; None is its mirror, once the answer is in. Switcher holds the searchable list: Switcher.Surface maps onto a Popover or a Sheet, Switcher.Options, Input, List, Item, Empty and Error search and list, and Switcher.Shortcuts binds keys to switch.

Import: @slices.sh/auth/headless

Capability: organizations

```tsx
GateProps
```

Alternate import: Organization.Active from @slices.sh/auth/headless/organization

Source: @slices.sh/auth/src/headless/organization/active.tsx
