# Switch or create a workspace

Search accessible organizations and switch the active workspace.

Import: @slices.sh/auth/headless

Public parts: 29

## 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
  User.SignedIn
    Organization.Active
      Organization.Switcher.Shortcuts
        Organization.Switcher.Options
          Organization.Switcher.Input
          Organization.Switcher.List
            Organization.Item
              Organization.Logo
              Organization.Name
              Organization.Role
            Organization.Switcher.Item
          Organization.Personal
          Organization.Create.Trigger
      Organization.Switcher.Surface
      Organization.Manage
  Organization.None
  Organization.Switcher.Empty
  Organization.Switcher.Error
  Organization.Create.Dialog open
    Organization.Create.Form
      Organization.Create.Label
      Organization.Create.Name
      Organization.Create.Submit
    Organization.Create.Error
  Organization.Loading
  Organization.Empty
  Organization.Error
```

## Minimal composition

```tsx
"use client"

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

export function Example() {
  return (
    <User.Root>
      <Organization.Switcher.Options>
        <Organization.Switcher.Input aria-label="Find a workspace" />
        <Organization.Switcher.List>
          <Organization.Switcher.Item />
        </Organization.Switcher.List>
        <Organization.Switcher.Empty>
          No matches.
        </Organization.Switcher.Empty>
      </Organization.Switcher.Options>
    </User.Root>
  )
}

```

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

## Organizations

```tsx
"use client"

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

export function Example() {
  return (
    <User.Root>
      <Organization.Item>
        <Organization.Logo />
        <Organization.Name />
      </Organization.Item>
      <Organization.Empty>No workspaces yet.</Organization.Empty>
    </User.Root>
  )
}

```

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

## Create organization

```tsx
"use client"

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

export function Example() {
  return (
    <User.Root>
      <Organization.Create.Form>
        <Organization.Create.Label>
          Workspace name
        </Organization.Create.Label>
        <Organization.Create.Name />
        <Organization.Create.Submit>
          Create workspace
        </Organization.Create.Submit>
        <Organization.Create.Error />
      </Organization.Create.Form>
    </User.Root>
  )
}

```

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

## Complete API reference

### Organization.Role

content

For the organization in scope. Name is text. Logo is <img alt=""> and nothing without a logo. Role gets { role }, the person's role in it. Manage is the host's link to the active organization's settings.

Import: @slices.sh/auth/headless

Organization.Role requires Organization.Item or Organization.Active.

```tsx
PartProps<null, { role: MemberRole | null }>
```

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

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

### Organization.Manage

content · Element: a

The host owns where settings for the currently active organization live.

Import: @slices.sh/auth/headless

```tsx
PartProps<"a">
```

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

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

### Organization.Item

action · Element: button

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
ItemProps

type ItemProps = PartProps<"button", { active: boolean; pending: boolean }>
```

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

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

### Organization.Personal

action · Element: button

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
PartProps<"button", { active: boolean }>
```

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

Source: @slices.sh/auth/src/headless/organization/personal.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

### Organization.None

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.None from @slices.sh/auth/headless/organization

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

### Organization.Name

content

For the organization in scope. Name is text. Logo is <img alt=""> and nothing without a logo. Role gets { role }, the person's role in it. Manage is the host's link to the active organization's settings.

Import: @slices.sh/auth/headless

Organization.Name must be inside <Organization.Item> or <Organization.Active>.

```tsx
PartProps<null>
```

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

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

### Organization.Logo

content · Element: img

For the organization in scope. Name is text. Logo is <img alt=""> and nothing without a logo. Role gets { role }, the person's role in it. Manage is the host's link to the active organization's settings.

Import: @slices.sh/auth/headless

Organization.Logo must be inside <Organization.Item> or <Organization.Active>.

```tsx
PartProps<"img">
```

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

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

### Organization.Loading

gate

Loading covers the first load only. Empty opens when the person belongs to none. Error gets { code, message }.

Import: @slices.sh/auth/headless

```tsx
GateProps
```

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

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

### Organization.Empty

gate

Loading covers the first load only. Empty opens when the person belongs to none. Error gets { code, message }.

Import: @slices.sh/auth/headless

```tsx
GateProps
```

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

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

### Organization.Error

gate

Loading covers the first load only. Empty opens when the person belongs to none. Error gets { code, message }.

Import: @slices.sh/auth/headless

```tsx
GateProps<{ code: AuthErrorCode; message: string }>
```

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

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

### Organization.Switcher.Shortcuts

scope

Organization.Switcher.Shortcuts provides state to the parts inside it.

Import: @slices.sh/auth/headless

```tsx
RootProps & { enabled?: boolean }
```

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

Source: @slices.sh/auth/src/headless/organization/switch-shortcuts.tsx

### Organization.Switcher.Surface

content

Organization.Switcher.Surface renders content from its current scope.

Import: @slices.sh/auth/headless

```tsx
PartProps<null>
```

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

Source: @slices.sh/auth/src/headless/organization/switcher-surface.tsx

### Organization.Switcher.Options

scope

Organization.Switcher.Options provides state to the parts inside it.

Import: @slices.sh/auth/headless

```tsx
RootProps
```

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

Source: @slices.sh/auth/src/headless/organization/search-root.tsx

### Organization.Switcher.Input

input · Element: input

Organization.Switcher.Input renders a input element.

Import: @slices.sh/auth/headless

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

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

Source: @slices.sh/auth/src/headless/organization/search-input.tsx

### Organization.Switcher.List

content · Element: div

Organization.Switcher.List renders a div element.

Import: @slices.sh/auth/headless

```tsx
PartProps<"div">
```

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

Source: @slices.sh/auth/src/headless/organization/search-list.tsx

### Organization.Switcher.Item

content · Element: div

Organization.Switcher.Item renders a div element.

Import: @slices.sh/auth/headless

Organization.Switcher.Item must be inside <Organization.Switcher.Options>.

```tsx
ItemProps

type ItemProps = PartProps<"div", { active: boolean; pending: boolean }>
```

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

Source: @slices.sh/auth/src/headless/organization/search-item.tsx

### Organization.Switcher.Empty

gate

Organization.Switcher.Empty conditionally renders its children.

Import: @slices.sh/auth/headless

Organization.Switcher.Empty must be inside <Organization.Switcher.Options>.

```tsx
GateProps
```

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

Source: @slices.sh/auth/src/headless/organization/search-empty.tsx

### Organization.Switcher.Error

gate

Organization.Switcher.Error conditionally renders its children.

Import: @slices.sh/auth/headless

```tsx
GateProps<{ code: AuthErrorCode; message: string }>
```

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

Source: @slices.sh/auth/src/headless/organization/search-error.tsx

### Organization.Create.Dialog

content

Organization.Create.Dialog renders content from its current scope.

Import: @slices.sh/auth/headless

```tsx
PartProps<null>
```

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

Source: @slices.sh/auth/src/headless/organization/create-dialog.tsx

### Organization.Create.Trigger

action · Element: button

Organization.Create.Trigger renders a button element.

Import: @slices.sh/auth/headless

```tsx
PartProps<"button", { query: string }>
```

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

Source: @slices.sh/auth/src/headless/organization/create-trigger.tsx

### Organization.Create.Form

action · Element: form

Organization.Create.Form renders a form element.

Import: @slices.sh/auth/headless

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

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

Source: @slices.sh/auth/src/headless/organization/create-form.tsx

### Organization.Create.Label

content · Element: label

Organization.Create.Label renders a label element.

Import: @slices.sh/auth/headless

Organization.Create.Label must be inside <Organization.Create.Form>.

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

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

Source: @slices.sh/auth/src/headless/organization/create-label.tsx

### Organization.Create.Name

input · Element: input

Organization.Create.Name renders a input element.

Import: @slices.sh/auth/headless

Organization.Create.Name must be inside <Organization.Create.Form>.

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

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

Source: @slices.sh/auth/src/headless/organization/create-name.tsx

### Organization.Create.Submit

action · Element: button

Organization.Create.Submit renders a button element.

Import: @slices.sh/auth/headless

Organization.Create.Submit must be inside <Organization.Create.Form>.

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

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

Source: @slices.sh/auth/src/headless/organization/create-submit.tsx

### Organization.Create.Error

content · Element: div

Organization.Create.Error renders a div element.

Import: @slices.sh/auth/headless

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

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

Source: @slices.sh/auth/src/headless/organization/create-error.tsx

### Principals.Known

gate

Known renders once the session and the organizations are known; nothing for a visitor or a backend without sessions.

Import: @slices.sh/auth/headless

```tsx
GateProps
```

Alternate import: Principals.Known from @slices.sh/auth/headless/principals

Source: @slices.sh/auth/src/headless/principals/known.tsx

### Principals.PersonalOption

content · Element: option

PersonalOption is an <option> for the viewer themselves, its value locked to their principal, with { name }. OrganizationOption repeats an <option> per organization the viewer belongs to, its name as default content, and renders nothing without organizations.

Import: @slices.sh/auth/headless

Capability: organizations

```tsx
OptionProps

type OptionProps = PartProps<"option", { name: string | null }>
```

Alternate import: Principals.PersonalOption from @slices.sh/auth/headless/principals

Source: @slices.sh/auth/src/headless/principals/personal-option.tsx

### Principals.OrganizationOption

content · Element: option

PersonalOption is an <option> for the viewer themselves, its value locked to their principal, with { name }. OrganizationOption repeats an <option> per organization the viewer belongs to, its name as default content, and renders nothing without organizations.

Import: @slices.sh/auth/headless

Capability: organizations

```tsx
OptionProps

type OptionProps = PartProps<"option", { name: string }>
```

Alternate import: Principals.OrganizationOption from @slices.sh/auth/headless/principals

Source: @slices.sh/auth/src/headless/principals/organization-option.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

### User.SignedIn

gate

SignedIn and SignedOut open once the session has answered, so nobody flashes signed out. Loading covers the first load only. SessionExpired opens when the session on screen has run out while its name and picture are still shown. ReadError is <p role="alert"> with { message } when the session could not be read, and Retry reads it again.

Import: @slices.sh/auth/headless

```tsx
GateProps
```

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

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

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

Source: @slices.sh/auth/src/headless/user/signed-in.tsx
