# Workspace settings

Edit a workspace's name, slug and logo.

Import: @slices.sh/auth/headless

Public parts: 28

## 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
    Organization.Profile.Form
      Organization.Profile.SlugFields
        Organization.Profile.LogoUpload
          Organization.Profile.Picture
          Organization.Profile.RemoveLogo
        Organization.Profile.Label
        Organization.Profile.Name
        Organization.Profile.Slug
        Organization.Profile.SlugStatus
        Organization.Profile.Save
        Organization.Profile.Saved
      Organization.Profile.Error
    Organization.Leave.Root
      Organization.Leave.Confirm
        Organization.Leave.Dialog
        Organization.Leave.Submit
        Organization.Leave.Cancel
        Organization.Leave.Error
      Organization.Leave.Request
    Organization.Delete.Root
      Organization.Delete.Confirm
        Organization.Delete.Dialog
        Organization.Delete.Label
        Organization.Delete.Name
        Organization.Delete.Submit
        Organization.Delete.Cancel
        Organization.Delete.Error
      Organization.Delete.Request
```

## Minimal composition

```tsx
"use client"

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

export function Example() {
  return (
    <User.Root>
      <Organization.Profile.Form>
        <Organization.Profile.Label field="name">
          Name
        </Organization.Profile.Label>
        <Organization.Profile.Name />
        <Organization.Profile.Save>
          Save workspace
        </Organization.Profile.Save>
        <Organization.Profile.Saved>Saved.</Organization.Profile.Saved>
        <Organization.Profile.Error />
      </Organization.Profile.Form>
    </User.Root>
  )
}

```

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

## Leave organization

```tsx
"use client"

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

export function Example() {
  return (
    <User.Root>
      <Organization.Leave.Root>
        <Organization.Leave.Request>
          Leave workspace
        </Organization.Leave.Request>
        <Organization.Leave.Confirm>
          <Organization.Leave.Submit>
            Confirm leave
          </Organization.Leave.Submit>
          <Organization.Leave.Cancel>Cancel</Organization.Leave.Cancel>
        </Organization.Leave.Confirm>
        <Organization.Leave.Error />
      </Organization.Leave.Root>
    </User.Root>
  )
}

```

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

## Delete organization

```tsx
"use client"

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

export function Example() {
  return (
    <User.Root>
      <Organization.Delete.Root>
        <Organization.Delete.Request>
          Delete workspace
        </Organization.Delete.Request>
        <Organization.Delete.Confirm>
          <Organization.Delete.Label>
            Workspace name
          </Organization.Delete.Label>
          <Organization.Delete.Name />
          <Organization.Delete.Submit>
            Confirm delete
          </Organization.Delete.Submit>
          <Organization.Delete.Cancel>Cancel</Organization.Delete.Cancel>
        </Organization.Delete.Confirm>
        <Organization.Delete.Error />
      </Organization.Delete.Root>
    </User.Root>
  )
}

```

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

## Complete API reference

### Organization.Profile.Slug

input · Element: input

Organization.Profile.Slug renders a input element.

Import: @slices.sh/auth/headless

Organization.Profile.Slug requires Organization.Profile.Form.

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

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

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

### Organization.Profile.SlugFields

gate

Organization.Profile.SlugFields conditionally renders its children.

Import: @slices.sh/auth/headless

Organization.Profile.SlugFields requires Organization.Profile.Form.

```tsx
GateProps
```

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

Source: @slices.sh/auth/src/headless/organization/profile/slug-fields.tsx

### Organization.Profile.SlugStatus

content · Element: p

Organization.Profile.SlugStatus renders a p element.

Import: @slices.sh/auth/headless

Organization.Profile.SlugStatus requires Organization.Profile.Form.

```tsx
PartProps<"p", { status: "loading" | "ready" | "error"; available: boolean | null }>
```

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

Source: @slices.sh/auth/src/headless/organization/profile/slug-status.tsx

### Organization.Profile.Form

action · Element: form

Organization.Profile.Form renders a form element.

Import: @slices.sh/auth/headless

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

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

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

### Organization.Profile.Label

content · Element: label

Organization.Profile.Label renders a label element.

Import: @slices.sh/auth/headless

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

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

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

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

### Organization.Profile.Name

input · Element: input

Organization.Profile.Name renders a input element.

Import: @slices.sh/auth/headless

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

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

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

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

### Organization.Profile.LogoUpload

content

The half of a logo upload that belongs to auth: what to do with the picture once something
else has stored it. The mirror of `User.Profile.ImageUpload`.

The **permalink**, never the signed URL a fresh upload also carries: a signed URL expires,
and an organization left pointing at one shows a broken logo a week later.

Import: @slices.sh/auth/headless

Organization.Profile.LogoUpload must be inside <Organization.Profile.Form>.

```tsx
PartProps<
    null,
    { save(uploaded: { permalink: string }): void; value: string; change(url: string): void }
  >
```

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

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

### Organization.Profile.Picture

content · Element: img

Organization.Profile.Picture renders a img element.

Import: @slices.sh/auth/headless

Organization.Profile.Picture must be inside <Organization.Profile.Form>.

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

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

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

### Organization.Profile.RemoveLogo

action · Element: button

Organization.Profile.RemoveLogo renders a button element.

Import: @slices.sh/auth/headless

Organization.Profile.RemoveLogo must be inside <Organization.Profile.Form>.

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

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

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

### Organization.Profile.Save

action · Element: button

Organization.Profile.Save renders a button element.

Import: @slices.sh/auth/headless

Organization.Profile.Save must be inside <Organization.Profile.Form>.

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

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

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

### Organization.Profile.Saved

gate

Organization.Profile.Saved conditionally renders its children.

Import: @slices.sh/auth/headless

Organization.Profile.Saved must be inside <Organization.Profile.Form>.

```tsx
GateProps
```

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

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

### Organization.Profile.Error

content · Element: div

Organization.Profile.Error renders a div element.

Import: @slices.sh/auth/headless

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

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

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

### Organization.Leave.Root

scope

Request arms the confirmation. Dialog maps it onto render={<AlertDialog />}; Escape and the backdrop disarm it. Confirm gates an in-page question instead. Cancel disarms.

Import: @slices.sh/auth/headless

```tsx
RootProps & AuthRootProps
```

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

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

### Organization.Leave.Request

action · Element: button

Request arms the confirmation. Dialog maps it onto render={<AlertDialog />}; Escape and the backdrop disarm it. Confirm gates an in-page question instead. Cancel disarms.

Import: @slices.sh/auth/headless

Organization.Leave.Request must be inside <Organization.Leave.Root>.

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

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

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

### Organization.Leave.Dialog

content

The confirmation, as whatever the screen makes a dialog out of.

`Confirm` puts the question in the page, which is enough for a row nobody leaves by
accident and not enough for this: leaving loses access to everything the organization
holds, and a question like that wants focus trapped, Escape as the way out and nothing
else clickable behind it — an alert dialog's job, not a gate's.

The part is the dialog's root and owns `open`, so the armed confirmation the buttons
already read is the one thing that opens and closes it. Escape and the backdrop come
back through `onOpenChange` and disarm it, rather than leaving a closed dialog still
believing somebody said yes.

Import: @slices.sh/auth/headless

Organization.Leave.Dialog must be inside <Organization.Leave.Root>.

```tsx
PartProps<null>
```

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

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

### Organization.Leave.Confirm

gate

Request arms the confirmation. Dialog maps it onto render={<AlertDialog />}; Escape and the backdrop disarm it. Confirm gates an in-page question instead. Cancel disarms.

Import: @slices.sh/auth/headless

Organization.Leave.Confirm must be inside <Organization.Leave.Root>.

```tsx
GateProps
```

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

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

### Organization.Leave.Cancel

action · Element: button

Request arms the confirmation. Dialog maps it onto render={<AlertDialog />}; Escape and the backdrop disarm it. Confirm gates an in-page question instead. Cancel disarms.

Import: @slices.sh/auth/headless

Organization.Leave.Cancel must be inside <Organization.Leave.Root>.

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

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

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

### Organization.Leave.Submit

action · Element: button

Submit leaves, with { pending }, disabled until armed. Error is <div role="alert"> with { code, message }; ONLY_OWNER when you are the last owner.

Import: @slices.sh/auth/headless

Organization.Leave.Submit must be inside <Organization.Leave.Root>.

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

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

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

### Organization.Leave.Error

content · Element: div

Submit leaves, with { pending }, disabled until armed. Error is <div role="alert"> with { code, message }; ONLY_OWNER when you are the last owner.

Import: @slices.sh/auth/headless

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

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

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

### Organization.Delete.Root

scope

As in Leave. Dialog and Cancel also empty the retype field.

Import: @slices.sh/auth/headless

```tsx
RootProps & AuthRootProps
```

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

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

### Organization.Delete.Request

action · Element: button

As in Leave. Dialog and Cancel also empty the retype field.

Import: @slices.sh/auth/headless

Organization.Delete.Request must be inside <Organization.Delete.Root>.

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

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

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

### Organization.Delete.Dialog

content

The confirmation, as whatever the screen makes a dialog out of.

The mirror of `Organization.Leave.Dialog`, for the deed that cannot be undone: the part
is the dialog's root and owns `open`, so the armed confirmation is the one thing that
opens and closes it. Closing by Escape or the backdrop clears the retyped name too —
asking again starts from an empty field, never from half a name left over.

Import: @slices.sh/auth/headless

Organization.Delete.Dialog must be inside <Organization.Delete.Root>.

```tsx
PartProps<null>
```

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

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

### Organization.Delete.Confirm

gate

As in Leave. Dialog and Cancel also empty the retype field.

Import: @slices.sh/auth/headless

Organization.Delete.Confirm must be inside <Organization.Delete.Root>.

```tsx
GateProps
```

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

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

### Organization.Delete.Label

content · Element: label

Label locks htmlFor to Name, an <input autocomplete="off"> invalid once what is typed can no longer become the name. Submit is disabled until armed and matching, with { pending }. Error gets { code, message }.

Import: @slices.sh/auth/headless

Organization.Delete.Label must be inside <Organization.Delete.Root>.

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

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

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

### Organization.Delete.Name

input · Element: input

Label locks htmlFor to Name, an <input autocomplete="off"> invalid once what is typed can no longer become the name. Submit is disabled until armed and matching, with { pending }. Error gets { code, message }.

Import: @slices.sh/auth/headless

Organization.Delete.Name must be inside <Organization.Delete.Root>.

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

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

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

### Organization.Delete.Cancel

action · Element: button

As in Leave. Dialog and Cancel also empty the retype field.

Import: @slices.sh/auth/headless

Organization.Delete.Cancel must be inside <Organization.Delete.Root>.

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

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

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

### Organization.Delete.Submit

action · Element: button

Label locks htmlFor to Name, an <input autocomplete="off"> invalid once what is typed can no longer become the name. Submit is disabled until armed and matching, with { pending }. Error gets { code, message }.

Import: @slices.sh/auth/headless

Organization.Delete.Submit must be inside <Organization.Delete.Root>.

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

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

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

### Organization.Delete.Error

content · Element: div

Label locks htmlFor to Name, an <input autocomplete="off"> invalid once what is typed can no longer become the name. Submit is disabled until armed and matching, with { pending }. Error gets { code, message }.

Import: @slices.sh/auth/headless

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

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

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