Skip to content
SLICES / TECHNICAL FOLIO Nº 001FULL-STACK ASSEMBLIES — OPEN SOURCE

Headless / Organizations

Workspace settings

Edit a workspace's name, slug and logo.

28 public parts · 3examples

@slices.sh/auth/headlessView as Markdown ↗

Your design. Our behavior.

Explore the tree to see what each part controls. Gates reveal the relevant state; roots outline their scope.

Behavior / inputGateGenericRenderer · not headlessScope
COMPONENT ANATOMYHover · focus · tap
ONE POSSIBLE DESIGNStatic preview

User.Root · Scope

User.RootSays 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.

Installation

Choose what your component needs. Run these commands in an initialized Next.js app.

0

Before you start

The steps below run in a Next.js app with shadcn set up (it has a components.json). In an existing Next.js app, such as one from create-next-app, set shadcn up first:

npx shadcn@latest init -t next -b base -p nova

Or start fresh: the slices CLI creates the app and sets it up.

npx slices.sh install
1

Install the service registry

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

npx add services
2

Install the headless library

npm

Add the React parts to your app, then compose them with your own elements.

npm install @slices.sh/auth@^0.1.10

Import from @slices.sh/auth/headless.

3

Install the service

Connect the parts to a provider. Skip this step if this service is already configured in your app.

Provider
Database
Authentication methods
Account capabilities
npx add auth.betterauth --auth-methods google --organizations

Adds service configuration and API routes to your app, with the provider dependencies. For a headless composition, add import "@/services" after "use client".

The slices CLI installs the service and provisions its required resources in one command.

Set BETTER_AUTH_URL to your app origin. Memory is for local development; use a persistent database for deployment. Neon and Supabase also require the migration script printed by the installer.

Provider setup notes
  • BETTER_AUTH_SECRET signs every session and is generated into .env.local while this installs (32 random bytes, base64, nothing leaves the machine). Changing it signs everyone out; in production set the same one on every instance.
  • The public origin (OAuth callbacks, the passkey domain) is the one each request comes to in development, and the deployment's own on Vercel. Elsewhere, or behind a proxy or a custom domain, set BETTER_AUTH_URL, such as https://acme.com.

Start small

Parts bring the behavior. Use their default elements, or supply your own with render.

example.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.

API reference

Every public part used by this experience is listed below. Gates render children without adding an element. Element parts accept native element props and render; function children receive the state declared in their props. Scopes provide context rather than a visual element.

Organization.Profile.Sluginput · <input>

Organization.Profile.Slug renders a input element.

Required scope

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

Props

2 documented props · Native <input> attributes supported

Organization.Profile.Slug props
PropTypeDefault
children

Content rendered inside this part.

ReactNode—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Profile.Slug from @slices.sh/auth/headless/organization

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

Organization.Profile.SlugFieldsgate

Organization.Profile.SlugFields conditionally renders its children.

Required scope

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

Props

1 documented props

Organization.Profile.SlugFields props
PropTypeDefault
children

Content rendered inside this part.

ReactNode—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Profile.SlugFields from @slices.sh/auth/headless/organization

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

Organization.Profile.SlugStatuscontent · <p>

Organization.Profile.SlugStatus renders a p element.

Required scope

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

Props

2 documented props · Native <p> attributes supported

Organization.Profile.SlugStatus props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { status: "loading" | "ready" | "error"; available: boolean | null }) => ReactNode)—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Profile.SlugStatus from @slices.sh/auth/headless/organization

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

Organization.Profile.Formaction · <form>

Organization.Profile.Form renders a form element.

Props

2 documented props · Native <form> attributes supported

Organization.Profile.Form props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { pending: boolean }) => ReactNode)—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Profile.Form from @slices.sh/auth/headless/organization

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

Organization.Profile.Labelcontent · <label>

Organization.Profile.Label renders a label element.

Required scope

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

Props

3 documented props · Native <label> attributes supported

Organization.Profile.Label props
PropTypeDefault
fieldRequired
keyof typeof FIELDS—
children

Content rendered inside this part.

ReactNode—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Profile.Label from @slices.sh/auth/headless/organization

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

Organization.Profile.Nameinput · <input>

Organization.Profile.Name renders a input element.

Required scope

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

Props

2 documented props · Native <input> attributes supported

Organization.Profile.Name props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { invalid: boolean }) => ReactNode)—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Profile.Name from @slices.sh/auth/headless/organization

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

Organization.Profile.LogoUploadcontent

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.

Required scope

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

Props

2 documented props

Organization.Profile.LogoUpload props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { save(uploaded: { permalink: string }): void; value: string; change(url: string): void }) => ReactNode)—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Profile.LogoUpload from @slices.sh/auth/headless/organization

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

Organization.Profile.Picturecontent · <img>

Organization.Profile.Picture renders a img element.

Required scope

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

Props

2 documented props · Native <img> attributes supported

Organization.Profile.Picture props
PropTypeDefault
children

Content rendered inside this part.

ReactNode—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Profile.Picture from @slices.sh/auth/headless/organization

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

Organization.Profile.Saveaction · <button>

Organization.Profile.Save renders a button element.

Required scope

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

Props

2 documented props · Native <button> attributes supported

Organization.Profile.Save props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { pending: boolean }) => ReactNode)—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Profile.Save from @slices.sh/auth/headless/organization

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

Organization.Profile.Savedgate

Organization.Profile.Saved conditionally renders its children.

Required scope

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

Props

1 documented props

Organization.Profile.Saved props
PropTypeDefault
children

Content rendered inside this part.

ReactNode—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Profile.Saved from @slices.sh/auth/headless/organization

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

Organization.Profile.Errorcontent · <div>

Organization.Profile.Error renders a div element.

Props

2 documented props · Native <div> attributes supported

Organization.Profile.Error props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { code: AuthErrorCode; message: string }) => ReactNode)—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Profile.Error from @slices.sh/auth/headless/organization

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

Organization.Leave.Rootscope

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.

Props

3 documented props

Organization.Leave.Root props
PropTypeDefault
config

The auth config, a slug from the registry (services/auth/<config>.config.ts). The nearest root's, else "default", when left out.

string—
enabled

Left out or true: required, and missing auth throws. "optional": renders its children and every part inside degrades without auth. false: off. A capability name such as "methods: password": requires auth with it.

Enabled—
children

Content rendered inside this part.

ReactNode—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Leave.Root from @slices.sh/auth/headless/organization

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

Organization.Leave.Requestaction · <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.

Required scope

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

Props

2 documented props · Native <button> attributes supported

Organization.Leave.Request props
PropTypeDefault
children

Content rendered inside this part.

ReactNode—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Leave.Request from @slices.sh/auth/headless/organization

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

Organization.Leave.Dialogcontent

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.

Required scope

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

Props

2 documented props

Organization.Leave.Dialog props
PropTypeDefault
children

Content rendered inside this part.

ReactNode—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Leave.Dialog from @slices.sh/auth/headless/organization

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

Organization.Leave.Confirmgate

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.

Required scope

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

Props

1 documented props

Organization.Leave.Confirm props
PropTypeDefault
children

Content rendered inside this part.

ReactNode—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Leave.Confirm from @slices.sh/auth/headless/organization

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

Organization.Leave.Cancelaction · <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.

Required scope

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

Props

2 documented props · Native <button> attributes supported

Organization.Leave.Cancel props
PropTypeDefault
children

Content rendered inside this part.

ReactNode—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Leave.Cancel from @slices.sh/auth/headless/organization

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

Organization.Leave.Submitaction · <button>

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

Required scope

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

Props

2 documented props · Native <button> attributes supported

Organization.Leave.Submit props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { pending: boolean }) => ReactNode)—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Leave.Submit from @slices.sh/auth/headless/organization

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

Organization.Leave.Errorcontent · <div>

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

Props

2 documented props · Native <div> attributes supported

Organization.Leave.Error props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { code: AuthErrorCode; message: string }) => ReactNode)—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Leave.Error from @slices.sh/auth/headless/organization

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

Organization.Delete.Rootscope

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

Props

3 documented props

Organization.Delete.Root props
PropTypeDefault
config

The auth config, a slug from the registry (services/auth/<config>.config.ts). The nearest root's, else "default", when left out.

string—
enabled

Left out or true: required, and missing auth throws. "optional": renders its children and every part inside degrades without auth. false: off. A capability name such as "methods: password": requires auth with it.

Enabled—
children

Content rendered inside this part.

ReactNode—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Delete.Root from @slices.sh/auth/headless/organization

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

Organization.Delete.Requestaction · <button>

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

Required scope

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

Props

2 documented props · Native <button> attributes supported

Organization.Delete.Request props
PropTypeDefault
children

Content rendered inside this part.

ReactNode—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Delete.Request from @slices.sh/auth/headless/organization

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

Organization.Delete.Dialogcontent

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.

Required scope

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

Props

2 documented props

Organization.Delete.Dialog props
PropTypeDefault
children

Content rendered inside this part.

ReactNode—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Delete.Dialog from @slices.sh/auth/headless/organization

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

Organization.Delete.Confirmgate

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

Required scope

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

Props

1 documented props

Organization.Delete.Confirm props
PropTypeDefault
children

Content rendered inside this part.

ReactNode—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Delete.Confirm from @slices.sh/auth/headless/organization

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

Organization.Delete.Labelcontent · <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 }.

Required scope

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

Props

2 documented props · Native <label> attributes supported

Organization.Delete.Label props
PropTypeDefault
children

Content rendered inside this part.

ReactNode—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Delete.Label from @slices.sh/auth/headless/organization

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

Organization.Delete.Nameinput · <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 }.

Required scope

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

Props

2 documented props · Native <input> attributes supported

Organization.Delete.Name props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { invalid: boolean }) => ReactNode)—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Delete.Name from @slices.sh/auth/headless/organization

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

Organization.Delete.Cancelaction · <button>

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

Required scope

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

Props

2 documented props · Native <button> attributes supported

Organization.Delete.Cancel props
PropTypeDefault
children

Content rendered inside this part.

ReactNode—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Delete.Cancel from @slices.sh/auth/headless/organization

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

Organization.Delete.Submitaction · <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 }.

Required scope

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

Props

2 documented props · Native <button> attributes supported

Organization.Delete.Submit props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { pending: boolean }) => ReactNode)—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Delete.Submit from @slices.sh/auth/headless/organization

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

Organization.Delete.Errorcontent · <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 }.

Props

2 documented props · Native <div> attributes supported

Organization.Delete.Error props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { code: AuthErrorCode; message: string }) => ReactNode)—
render

Replace the default element with your own while retaining the part’s behavior and accessibility.

ReactElement—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Delete.Error from @slices.sh/auth/headless/organization

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

User.Rootscope

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.

Props

3 documented props

User.Root props
PropTypeDefault
config

The auth config, a slug from the registry (services/auth/<config>.config.ts). The nearest root's, else "default", when left out.

string—
enabled

Left out or true: required, and missing auth throws. "optional": renders its children and every part inside degrades without auth. false: off. A capability name such as "methods: password": requires auth with it.

Enabled—
children

Content rendered inside this part.

ReactNode—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • User.Root from @slices.sh/auth/headless/user
  • User.Root from @slices.sh/auth/headless/passkeys
  • User.Root from @slices.sh/auth/headless/two-factor

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

Organization.Activegate

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.

Capability: organizations

Props

1 documented props

Organization.Active props
PropTypeDefault
children

Content rendered inside this part.

ReactNode—

Required props are marked. A dash means no explicit default is declared in this component.

Alternate public imports
  • Organization.Active from @slices.sh/auth/headless/organization

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

When something is missing

No service configuration
Install the auth setup below and import "@/services". Required service roots report missing setup; gates do not replace that configuration.
Missing capability or parent scope
Follow each part’s context requirements and the example composition. Capability-aware controls hide when the provider cannot perform their action.
Read the setup guide →