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

Headless / Account

Profile and contact details

Edit the current account's name and picture, with save and failure states.

51 public parts · 4examples

@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

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 } from "@slices.sh/auth/headless"export function Example() {  return (    <User.Root>      <User.Profile.Form>        <User.Profile.Label field="name">Name</User.Profile.Label>        <User.Profile.Name />        <User.Profile.Save>Save profile</User.Profile.Save>        <User.Profile.Saved>Saved.</User.Profile.Saved>        <User.Profile.Error />      </User.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.

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

User.SignedIngate

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.

Props

1 documented props

User.SignedIn 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
  • User.SignedIn from @slices.sh/auth/headless/user
  • User.SignedIn from @slices.sh/auth/headless/passkeys
  • User.SignedIn from @slices.sh/auth/headless/two-factor

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

User.SessionExpiredgate

The session on screen has run out. Shown while the avatar and the name are still there, because they are: the read that loaded them succeeded, and what changed is the clock. Everything offered from here is going to come back `SIGNED_OUT`, so it is worth saying so before somebody tries.

Props

1 documented props

User.SessionExpired 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
  • User.SessionExpired from @slices.sh/auth/headless/user
  • User.SessionExpired from @slices.sh/auth/headless/passkeys
  • User.SessionExpired from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/session-expired.tsx

User.Loadinggate

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.

Props

1 documented props

User.Loading 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
  • User.Loading from @slices.sh/auth/headless/user
  • User.Loading from @slices.sh/auth/headless/passkeys
  • User.Loading from @slices.sh/auth/headless/two-factor

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

User.ReadErrorcontent · <p>

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.

Props

2 documented props · Native <p> attributes supported

User.ReadError props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { 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
  • User.ReadError from @slices.sh/auth/headless/user
  • User.ReadError from @slices.sh/auth/headless/passkeys
  • User.ReadError from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/read-error.tsx

User.Retryaction · <button>

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.

Props

2 documented props · Native <button> attributes supported

User.Retry 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
  • User.Retry from @slices.sh/auth/headless/user
  • User.Retry from @slices.sh/auth/headless/passkeys
  • User.Retry from @slices.sh/auth/headless/two-factor

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

User.Imagecontent · <img>

Image is <img alt=""> with src locked to the stored image, else Gravatar, and nothing when neither exists or it fails to load. Initials are two letters from the name, else the address. Name and Phone render nothing when absent. Email is the address.

Props

2 documented props · Native <img> attributes supported

User.Image 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
  • User.Image from @slices.sh/auth/headless/user
  • User.Image from @slices.sh/auth/headless/passkeys
  • User.Image from @slices.sh/auth/headless/two-factor

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

User.Initialscontent

Image is <img alt=""> with src locked to the stored image, else Gravatar, and nothing when neither exists or it fails to load. Initials are two letters from the name, else the address. Name and Phone render nothing when absent. Email is the address.

Props

2 documented props

User.Initials 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
  • User.Initials from @slices.sh/auth/headless/user
  • User.Initials from @slices.sh/auth/headless/passkeys
  • User.Initials from @slices.sh/auth/headless/two-factor

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

User.Namecontent

Image is <img alt=""> with src locked to the stored image, else Gravatar, and nothing when neither exists or it fails to load. Initials are two letters from the name, else the address. Name and Phone render nothing when absent. Email is the address.

Props

2 documented props

User.Name 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
  • User.Name from @slices.sh/auth/headless/user
  • User.Name from @slices.sh/auth/headless/passkeys
  • User.Name from @slices.sh/auth/headless/two-factor

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

User.Emailcontent

Image is <img alt=""> with src locked to the stored image, else Gravatar, and nothing when neither exists or it fails to load. Initials are two letters from the name, else the address. Name and Phone render nothing when absent. Email is the address.

Props

2 documented props

User.Email 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
  • User.Email from @slices.sh/auth/headless/user
  • User.Email from @slices.sh/auth/headless/passkeys
  • User.Email from @slices.sh/auth/headless/two-factor

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

User.Phonecontent

Image is <img alt=""> with src locked to the stored image, else Gravatar, and nothing when neither exists or it fails to load. Initials are two letters from the name, else the address. Name and Phone render nothing when absent. Email is the address.

Props

2 documented props

User.Phone 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
  • User.Phone from @slices.sh/auth/headless/user
  • User.Phone from @slices.sh/auth/headless/passkeys
  • User.Phone from @slices.sh/auth/headless/two-factor

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

User.Profile.Formaction · <form>

Form holds the fields, prevents the page submit and gives { pending }; nothing when the backend changes neither field. Label takes field="name" | "image" and renders nothing when its field is hidden. Name starts at the stored name, with { invalid }.

Capability: profile: name

Props

2 documented props · Native <form> attributes supported

User.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
  • User.Profile.Form from @slices.sh/auth/headless/user
  • User.Profile.Form from @slices.sh/auth/headless/passkeys
  • User.Profile.Form from @slices.sh/auth/headless/two-factor

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

User.Profile.Labelcontent · <label>

Form holds the fields, prevents the page submit and gives { pending }; nothing when the backend changes neither field. Label takes field="name" | "image" and renders nothing when its field is hidden. Name starts at the stored name, with { invalid }.

Capability: profile: name

Required scope

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

Props

3 documented props · Native <label> attributes supported

User.Profile.Label props
PropTypeDefault
fieldRequired
ProfileField & 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
  • User.Profile.Label from @slices.sh/auth/headless/user
  • User.Profile.Label from @slices.sh/auth/headless/passkeys
  • User.Profile.Label from @slices.sh/auth/headless/two-factor

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

User.Profile.Nameinput · <input>

Form holds the fields, prevents the page submit and gives { pending }; nothing when the backend changes neither field. Label takes field="name" | "image" and renders nothing when its field is hidden. Name starts at the stored name, with { invalid }.

Capability: profile: name

Required scope

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

Props

2 documented props · Native <input> attributes supported

User.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
  • User.Profile.Name from @slices.sh/auth/headless/user
  • User.Profile.Name from @slices.sh/auth/headless/passkeys
  • User.Profile.Name from @slices.sh/auth/headless/two-factor

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

User.Profile.ImageUploadcontent

`save` stages a legacy upload; `change` persists a cropped portrait independently of other draft fields.

Capability: profile: image

Required scope

User.Profile.ImageUpload must be inside <User.Profile.Form>.

Props

2 documented props

User.Profile.ImageUpload 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
  • User.Profile.ImageUpload from @slices.sh/auth/headless/user
  • User.Profile.ImageUpload from @slices.sh/auth/headless/passkeys
  • User.Profile.ImageUpload from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/profile/image-upload.tsx

User.Profile.Picturecontent · <img>

ImageUpload is auth's half of an avatar upload: its function child gets { save }, which keeps the permalink an Avatar.Root or Upload.Root reports. Auth stores no bytes. Picture shows what a save would leave: the edit, else the stored picture, else Gravatar. RemoveImage renders nothing while there is no picture.

Capability: profile: image

Required scope

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

Props

2 documented props · Native <img> attributes supported

User.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
  • User.Profile.Picture from @slices.sh/auth/headless/user
  • User.Profile.Picture from @slices.sh/auth/headless/passkeys
  • User.Profile.Picture from @slices.sh/auth/headless/two-factor

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

User.Profile.RemoveImageaction · <button>

ImageUpload is auth's half of an avatar upload: its function child gets { save }, which keeps the permalink an Avatar.Root or Upload.Root reports. Auth stores no bytes. Picture shows what a save would leave: the edit, else the stored picture, else Gravatar. RemoveImage renders nothing while there is no picture.

Capability: profile: image

Required scope

User.Profile.RemoveImage must be inside <User.Profile.Form>.

Props

2 documented props · Native <button> attributes supported

User.Profile.RemoveImage 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
  • User.Profile.RemoveImage from @slices.sh/auth/headless/user
  • User.Profile.RemoveImage from @slices.sh/auth/headless/passkeys
  • User.Profile.RemoveImage from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/profile/remove-image.tsx

User.Profile.Saveaction · <button>

Save is the submit with { pending }, disabled while nothing changed. Saved opens after a save until a field changes. Error is <div role="alert"> with { code, message }.

Required scope

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

Props

2 documented props · Native <button> attributes supported

User.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
  • User.Profile.Save from @slices.sh/auth/headless/user
  • User.Profile.Save from @slices.sh/auth/headless/passkeys
  • User.Profile.Save from @slices.sh/auth/headless/two-factor

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

User.Profile.Savedgate

Save is the submit with { pending }, disabled while nothing changed. Saved opens after a save until a field changes. Error is <div role="alert"> with { code, message }.

Required scope

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

Props

1 documented props

User.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
  • User.Profile.Saved from @slices.sh/auth/headless/user
  • User.Profile.Saved from @slices.sh/auth/headless/passkeys
  • User.Profile.Saved from @slices.sh/auth/headless/two-factor

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

User.Profile.Errorcontent · <div>

Save is the submit with { pending }, disabled while nothing changed. Saved opens after a save until a field changes. Error is <div role="alert"> with { code, message }.

Props

2 documented props · Native <div> attributes supported

User.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
  • User.Profile.Error from @slices.sh/auth/headless/user
  • User.Profile.Error from @slices.sh/auth/headless/passkeys
  • User.Profile.Error from @slices.sh/auth/headless/two-factor

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

User.ChangeEmail.Rootscope

The section, only where the backend can change an address. Every `ChangeEmail` part already renders nothing without the capability, but the heading and the hint around them are pattern code, not parts — so on a backend that cannot do it an installed `email=1` drew "Email address" over an empty space. A gate around the whole section is what `Passkeys.Root` does for the same reason. It also holds whether the person has asked to change their address: until they do, there is a sentence and nothing to fill in, which is what `Edit` and `Editing` divide.

Props

3 documented props

User.ChangeEmail.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.ChangeEmail.Root from @slices.sh/auth/headless/user
  • User.ChangeEmail.Root from @slices.sh/auth/headless/passkeys
  • User.ChangeEmail.Root from @slices.sh/auth/headless/two-factor

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

User.ChangeEmail.Editaction · <button>

What opens the form: the address itself, as something to press. The address was drawn as prose with a form already under it, so the one thing anybody tries — pressing their own address — did nothing at all. It is a button now, and the form it opens stands in its place.

Required scope

User.ChangeEmail.Edit must be inside <User.ChangeEmail.Root>.

Props

2 documented props · Native <button> attributes supported

User.ChangeEmail.Edit 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
  • User.ChangeEmail.Edit from @slices.sh/auth/headless/user
  • User.ChangeEmail.Edit from @slices.sh/auth/headless/passkeys
  • User.ChangeEmail.Edit from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-email/edit.tsx

User.ChangeEmail.Editinggate

Edit is the address as a button: pressing it opens the form and it stands aside. Editing gates the form once Edit was pressed.

Required scope

User.ChangeEmail.Editing must be inside <User.ChangeEmail.Root>.

Props

1 documented props

User.ChangeEmail.Editing 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
  • User.ChangeEmail.Editing from @slices.sh/auth/headless/user
  • User.ChangeEmail.Editing from @slices.sh/auth/headless/passkeys
  • User.ChangeEmail.Editing from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-email/editing.tsx

User.ChangeEmail.Formaction · <form>

Form holds the address and the code with { pending }. Label locks htmlFor. Input is <input type="email"> with { invalid } once the backend refuses it. Submit is disabled until the address is valid and differs from the current one.

Props

2 documented props · Native <form> attributes supported

User.ChangeEmail.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
  • User.ChangeEmail.Form from @slices.sh/auth/headless/user
  • User.ChangeEmail.Form from @slices.sh/auth/headless/passkeys
  • User.ChangeEmail.Form from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-email/form.tsx

User.ChangeEmail.Labelcontent · <label>

Form holds the address and the code with { pending }. Label locks htmlFor. Input is <input type="email"> with { invalid } once the backend refuses it. Submit is disabled until the address is valid and differs from the current one.

Required scope

User.ChangeEmail.Label must be inside <User.ChangeEmail.Form>.

Props

2 documented props · Native <label> attributes supported

User.ChangeEmail.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
  • User.ChangeEmail.Label from @slices.sh/auth/headless/user
  • User.ChangeEmail.Label from @slices.sh/auth/headless/passkeys
  • User.ChangeEmail.Label from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-email/label.tsx

User.ChangeEmail.Inputinput · <input>

Form holds the address and the code with { pending }. Label locks htmlFor. Input is <input type="email"> with { invalid } once the backend refuses it. Submit is disabled until the address is valid and differs from the current one.

Required scope

User.ChangeEmail.Input must be inside <User.ChangeEmail.Form>.

Props

2 documented props · Native <input> attributes supported

User.ChangeEmail.Input 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
  • User.ChangeEmail.Input from @slices.sh/auth/headless/user
  • User.ChangeEmail.Input from @slices.sh/auth/headless/passkeys
  • User.ChangeEmail.Input from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-email/input.tsx

User.ChangeEmail.Submitaction · <button>

Form holds the address and the code with { pending }. Label locks htmlFor. Input is <input type="email"> with { invalid } once the backend refuses it. Submit is disabled until the address is valid and differs from the current one.

Required scope

User.ChangeEmail.Submit must be inside <User.ChangeEmail.Form>.

Props

2 documented props · Native <button> attributes supported

User.ChangeEmail.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
  • User.ChangeEmail.Submit from @slices.sh/auth/headless/user
  • User.ChangeEmail.Submit from @slices.sh/auth/headless/passkeys
  • User.ChangeEmail.Submit from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-email/submit.tsx

User.ChangeEmail.Pendinggate

While the new address has a code outstanding. The address is not theirs until they prove it, and the only thing that can prove it is something delivered to it — so the code goes to the new address, and this is the step where it is entered.

Required scope

User.ChangeEmail.Pending must be inside <User.ChangeEmail.Form>.

Props

1 documented props

User.ChangeEmail.Pending props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { email: string }) => ReactNode)—

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

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

Source: @slices.sh/auth/src/headless/user/change-email/pending.tsx

User.ChangeEmail.Destinationcontent

The address the code went to, which is the new one rather than the one on the account.

Required scope

User.ChangeEmail.Destination must be inside <User.ChangeEmail.Form>.

Props

2 documented props

User.ChangeEmail.Destination 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
  • User.ChangeEmail.Destination from @slices.sh/auth/headless/user
  • User.ChangeEmail.Destination from @slices.sh/auth/headless/passkeys
  • User.ChangeEmail.Destination from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-email/destination.tsx

User.ChangeEmail.Codeinput · <input>

Pending opens with { email } while a code is outstanding. Destination is the new address. Code is a numeric one-time-code input that checks itself on the last digit, read-only while it does. Verify does the same for a pasted code.

Required scope

User.ChangeEmail.Code must be inside <User.ChangeEmail.Form>.

Props

2 documented props · Native <input> attributes supported

User.ChangeEmail.Code 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
  • User.ChangeEmail.Code from @slices.sh/auth/headless/user
  • User.ChangeEmail.Code from @slices.sh/auth/headless/passkeys
  • User.ChangeEmail.Code from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-email/code.tsx

User.ChangeEmail.Verifyaction · <button>

Spending the code, for anyone who does not reach its last digit by typing. The field checks itself on that digit, so this is the same deed rather than another one — for a pasted code, for a code whose last digit was corrected, and for anybody who wants a button to press.

Required scope

User.ChangeEmail.Verify must be inside <User.ChangeEmail.Form>.

Props

2 documented props · Native <button> attributes supported

User.ChangeEmail.Verify 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
  • User.ChangeEmail.Verify from @slices.sh/auth/headless/user
  • User.ChangeEmail.Verify from @slices.sh/auth/headless/passkeys
  • User.ChangeEmail.Verify from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-email/verify.tsx

User.ChangeEmail.Changedgate

Changed opens with { email } once the address is the account's. Error is <div role="alert"> with { code, message } for whichever failed last.

Required scope

User.ChangeEmail.Changed must be inside <User.ChangeEmail.Form>.

Props

1 documented props

User.ChangeEmail.Changed props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { email: string }) => ReactNode)—

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

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

Source: @slices.sh/auth/src/headless/user/change-email/changed.tsx

User.ChangeEmail.Errorcontent · <div>

Changed opens with { email } once the address is the account's. Error is <div role="alert"> with { code, message } for whichever failed last.

Props

2 documented props · Native <div> attributes supported

User.ChangeEmail.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
  • User.ChangeEmail.Error from @slices.sh/auth/headless/user
  • User.ChangeEmail.Error from @slices.sh/auth/headless/passkeys
  • User.ChangeEmail.Error from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-email/error.tsx

User.ChangePhone.Rootscope

The section, only where the backend can text a code to a new number. The heading around the parts is pattern code, not a part, so on a backend that cannot do it an installed `phone=1` would draw a title over an empty space: a gate around the whole section is what `ChangeEmail.Root` does for the same reason. It also holds whether the person has asked to change their number, which `Edit` and `Editing` divide.

Props

3 documented props

User.ChangePhone.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.ChangePhone.Root from @slices.sh/auth/headless/user
  • User.ChangePhone.Root from @slices.sh/auth/headless/passkeys
  • User.ChangePhone.Root from @slices.sh/auth/headless/two-factor

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

User.ChangePhone.Editaction · <button>

What opens the form: the number itself, as something to press. The children get the number on the account, or `null` for an account that has none yet, which wants another sentence than "change".

Required scope

User.ChangePhone.Edit must be inside <User.ChangePhone.Root>.

Props

2 documented props · Native <button> attributes supported

User.ChangePhone.Edit props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { phoneNumber: string | 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
  • User.ChangePhone.Edit from @slices.sh/auth/headless/user
  • User.ChangePhone.Edit from @slices.sh/auth/headless/passkeys
  • User.ChangePhone.Edit from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-phone/edit.tsx

User.ChangePhone.Editinggate

Edit is the number as a button, with { phoneNumber }, which is null for an account without one, so it can say add. Editing gates the form.

Required scope

User.ChangePhone.Editing must be inside <User.ChangePhone.Root>.

Props

1 documented props

User.ChangePhone.Editing 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
  • User.ChangePhone.Editing from @slices.sh/auth/headless/user
  • User.ChangePhone.Editing from @slices.sh/auth/headless/passkeys
  • User.ChangePhone.Editing from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-phone/editing.tsx

User.ChangePhone.Formaction · <form>

Form holds the number and the code. PhoneRoot is Phone.Root over the form's field, taking defaultCountry, allowedCountries and locale; Phone.Country parts and Phone.Blocked go inside it. Label locks htmlFor to Input, which is Phone.Input with { invalid, status }. Submit is disabled until the number is valid, allowed and new.

Props

2 documented props · Native <form> attributes supported

User.ChangePhone.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
  • User.ChangePhone.Form from @slices.sh/auth/headless/user
  • User.ChangePhone.Form from @slices.sh/auth/headless/passkeys
  • User.ChangePhone.Form from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-phone/form.tsx

User.ChangePhone.PhoneRootscope

The new number and its country list, kept in the form's field as an E.164 number or as nothing at all. Renders nothing itself. The form's field keeps the id, so `ChangePhone.Label` finds the number's input without knowing a country list is there.

Required scope

User.ChangePhone.PhoneRoot must be inside <User.ChangePhone.Form>.

Props

4 documented props

User.ChangePhone.PhoneRoot props
PropTypeDefault
defaultCountry

Where a number typed without a + is read until a country is picked. ISO 3166-1 alpha-2.

string—
allowedCountries

The countries a number may be in, which are also the ones the list offers. Without it, every country.

readonly string[]—
locale
string—
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.ChangePhone.PhoneRoot from @slices.sh/auth/headless/user
  • User.ChangePhone.PhoneRoot from @slices.sh/auth/headless/passkeys
  • User.ChangePhone.PhoneRoot from @slices.sh/auth/headless/two-factor

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

User.ChangePhone.Labelcontent · <label>

Form holds the number and the code. PhoneRoot is Phone.Root over the form's field, taking defaultCountry, allowedCountries and locale; Phone.Country parts and Phone.Blocked go inside it. Label locks htmlFor to Input, which is Phone.Input with { invalid, status }. Submit is disabled until the number is valid, allowed and new.

Required scope

User.ChangePhone.Label must be inside <User.ChangePhone.Form>.

Props

2 documented props · Native <label> attributes supported

User.ChangePhone.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
  • User.ChangePhone.Label from @slices.sh/auth/headless/user
  • User.ChangePhone.Label from @slices.sh/auth/headless/passkeys
  • User.ChangePhone.Label from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-phone/label.tsx

User.ChangePhone.Inputinput · <input>

The new number, typed in its country's own format. It sits inside `ChangePhone.PhoneRoot`, which is where the country list is too, and says "invalid" for a refusal the number's own reading cannot know about: a number that is taken, or a country nobody may text.

Required scope

User.ChangePhone.Input must be inside <User.ChangePhone.Form>.

Props

3 documented props · Native <input> attributes supported

User.ChangePhone.Input props
PropTypeDefault
invalidMessage
string—
children

Content rendered inside this part.

ReactNode | ((state: { invalid: boolean; status: PhoneStatus }) => 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
  • User.ChangePhone.Input from @slices.sh/auth/headless/user
  • User.ChangePhone.Input from @slices.sh/auth/headless/passkeys
  • User.ChangePhone.Input from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-phone/input.tsx

User.ChangePhone.Submitaction · <button>

Form holds the number and the code. PhoneRoot is Phone.Root over the form's field, taking defaultCountry, allowedCountries and locale; Phone.Country parts and Phone.Blocked go inside it. Label locks htmlFor to Input, which is Phone.Input with { invalid, status }. Submit is disabled until the number is valid, allowed and new.

Required scope

User.ChangePhone.Submit must be inside <User.ChangePhone.Form>.

Props

2 documented props · Native <button> attributes supported

User.ChangePhone.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
  • User.ChangePhone.Submit from @slices.sh/auth/headless/user
  • User.ChangePhone.Submit from @slices.sh/auth/headless/passkeys
  • User.ChangePhone.Submit from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-phone/submit.tsx

User.ChangePhone.Pendinggate

While the new number has a code outstanding. The number is not theirs until they prove it, and the only thing that can prove it is something delivered to it: the code goes to the new number, and this is the step where it is entered.

Required scope

User.ChangePhone.Pending must be inside <User.ChangePhone.Form>.

Props

1 documented props

User.ChangePhone.Pending props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { phoneNumber: string }) => ReactNode)—

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

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

Source: @slices.sh/auth/src/headless/user/change-phone/pending.tsx

User.ChangePhone.Destinationcontent

The number the code went to, which is the new one rather than the one on the account.

Required scope

User.ChangePhone.Destination must be inside <User.ChangePhone.Form>.

Props

2 documented props

User.ChangePhone.Destination 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
  • User.ChangePhone.Destination from @slices.sh/auth/headless/user
  • User.ChangePhone.Destination from @slices.sh/auth/headless/passkeys
  • User.ChangePhone.Destination from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-phone/destination.tsx

User.ChangePhone.Codeinput · <input>

Pending opens with { phoneNumber } while a code is outstanding. Destination, Code and Verify behave as in ChangeEmail.

Required scope

User.ChangePhone.Code must be inside <User.ChangePhone.Form>.

Props

2 documented props · Native <input> attributes supported

User.ChangePhone.Code 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
  • User.ChangePhone.Code from @slices.sh/auth/headless/user
  • User.ChangePhone.Code from @slices.sh/auth/headless/passkeys
  • User.ChangePhone.Code from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-phone/code.tsx

User.ChangePhone.Verifyaction · <button>

Spending the code, for anyone who does not reach its last digit by typing: a pasted code, a code whose last digit was corrected, and anybody who wants a button to press.

Required scope

User.ChangePhone.Verify must be inside <User.ChangePhone.Form>.

Props

2 documented props · Native <button> attributes supported

User.ChangePhone.Verify 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
  • User.ChangePhone.Verify from @slices.sh/auth/headless/user
  • User.ChangePhone.Verify from @slices.sh/auth/headless/passkeys
  • User.ChangePhone.Verify from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-phone/verify.tsx

User.ChangePhone.Changedgate

Changed opens with { phoneNumber }. Error reports whichever failed last.

Required scope

User.ChangePhone.Changed must be inside <User.ChangePhone.Form>.

Props

1 documented props

User.ChangePhone.Changed props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { phoneNumber: string }) => ReactNode)—

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

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

Source: @slices.sh/auth/src/headless/user/change-phone/changed.tsx

User.ChangePhone.Errorcontent · <div>

Changed opens with { phoneNumber }. Error reports whichever failed last.

Props

2 documented props · Native <div> attributes supported

User.ChangePhone.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
  • User.ChangePhone.Error from @slices.sh/auth/headless/user
  • User.ChangePhone.Error from @slices.sh/auth/headless/passkeys
  • User.ChangePhone.Error from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/user/change-phone/error.tsx

Settings.Framecontent

Frame maps onto render={<Tabs />} and sets data-view="list" | "section" for a narrow panel. Back returns from the open section to the list; draw it only where the panel is too narrow for both.

Props

8 documented props

Settings.Frame props
PropTypeDefault
section
string—
defaultSection
string—
navigation

URL mode reads ?section= and popstate; the host handles navigation in onSectionChange.

"state" | "url"—
sectionsRequired
readonly string[]—
orientation
"horizontal" | "vertical"—
ref
Ref<HTMLElement>—
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
  • Settings.Frame from @slices.sh/auth/headless/settings

Source: @slices.sh/auth/src/headless/settings/frame.tsx

Settings.Backaction · <button>

Back to the list of sections, on a panel too narrow to show it beside the open one. It is drawn at every width: whether there is room for both is the layout's to say, from the panel's own width, so the button's styles hide it where the list is already in view.

Required scope

Settings.Back must be inside <Settings.Frame>.

Props

2 documented props · Native <button> attributes supported

Settings.Back 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
  • Settings.Back from @slices.sh/auth/headless/settings

Source: @slices.sh/auth/src/headless/settings/back.tsx

Layout.Narrowgate · Generic

Narrow renders on a phone-shaped window, Wide otherwise. Exactly one renders.

Props

1 documented props

Layout.Narrow 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
  • Layout.Narrow from @slices.sh/auth/headless/layout

Source: @slices.sh/auth/src/headless/layout/narrow.tsx

Layout.Widegate · Generic

Narrow renders on a phone-shaped window, Wide otherwise. Exactly one renders.

Props

1 documented props

Layout.Wide 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
  • Layout.Wide from @slices.sh/auth/headless/layout

Source: @slices.sh/auth/src/headless/layout/wide.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 →