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

Headless / Authentication

Recover access

Request a password reset, choose a new password and handle recovery states.

16 public parts · 1examples

@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

SignIn.Root · Scope

SignIn.RootStep gates on name and accepts render={<MotionPanel />} to animate; without render it is a wrapper-free gate. Status is the one polite live region, mounted for the card's life, that speaks waits and the attempt budget. Viewport is a plain container for animated steps. Error is <div role="alert"> with { code, message }, only for the current step's last failed action. RetryAfter gets { seconds } while a rate limit holds. Restart returns to identify and drops the challenge.

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 --password-reset

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 { SignIn } from "@slices.sh/auth/headless"export function Example() {  return (    <SignIn.Root>      <SignIn.Step name="identify">        <SignIn.ForgotPassword>Forgot password?</SignIn.ForgotPassword>      </SignIn.Step>      <SignIn.Step name="reset-request">        <SignIn.Form>          <SignIn.Email aria-label="Email" />          <SignIn.RequestReset>Send reset link</SignIn.RequestReset>          <SignIn.ResetSent>Check your inbox.</SignIn.ResetSent>        </SignIn.Form>      </SignIn.Step>      <SignIn.Step name="reset">        <SignIn.Form>          <SignIn.NewPassword aria-label="New password" />          <SignIn.PasswordStrength />          <SignIn.SubmitReset>Save password</SignIn.SubmitReset>        </SignIn.Form>      </SignIn.Step>      <SignIn.ResetDone>Password changed.</SignIn.ResetDone>      <SignIn.Error />    </SignIn.Root>  )}

Enable password-reset with a provider that sends reset links. The link returns to this page with a reset token.

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.

SignIn.PasswordStrengthcontent · <div>

The meter over a new password: `data-level` is the score, 0 to 4, and absent until one is known. Absent over an empty field too. A meter that says "very weak" before anything has been typed is a screen telling somebody off for not having started.

Capability: methods: password

Required scope

PasswordStrength must be inside <SignIn.Root> or <SignUp.Root>.

Props

2 documented props · Native <div> attributes supported

SignIn.PasswordStrength props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { score: number | null; levels: number }) => 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
  • Strength from @slices.sh/auth/headless/password-settings
  • SignIn.PasswordStrength from @slices.sh/auth/headless/sign-in
  • SignIn.PasswordStrength from @slices.sh/auth/headless/passkeys
  • SignIn.PasswordStrength from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/sign-in/password-strength.tsx

SignIn.StrengthSegmentcontent · <span>

One bar of the meter, repeated once per level. The count comes from the estimator's own scale rather than from the card, so a skin cannot draw five bars over a score that only goes to four.

Capability: methods: password

Required scope

StrengthSegment must be inside <SignIn.Root> or <SignUp.Root>.

Props

2 documented props · Native <span> attributes supported

SignIn.StrengthSegment props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { index: number; filled: 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
  • StrengthSegment from @slices.sh/auth/headless/password-settings
  • SignIn.StrengthSegment from @slices.sh/auth/headless/sign-in
  • SignIn.StrengthSegment from @slices.sh/auth/headless/passkeys
  • SignIn.StrengthSegment from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/sign-in/strength-segment.tsx

SignIn.PasswordFeedbackcontent · <p>

What the estimator would say about this password, in its own words. Its sentences come from the language pack, so they are already the person's language and are not repeated in `copy`. Absent until there is something to say: a strong password earns no advice, and an empty field has not been judged.

Capability: methods: password

Required scope

PasswordFeedback must be inside <SignIn.Root> or <SignUp.Root>.

Props

2 documented props · Native <p> attributes supported

SignIn.PasswordFeedback props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { warning: string | null; suggestions: readonly 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
  • Feedback from @slices.sh/auth/headless/password-settings
  • SignIn.PasswordFeedback from @slices.sh/auth/headless/sign-in
  • SignIn.PasswordFeedback from @slices.sh/auth/headless/passkeys
  • SignIn.PasswordFeedback from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/sign-in/password-feedback.tsx

SignIn.PasswordRequirementcontent · <li>

One rule a new password has to meet, said out loud rather than discovered by being refused. The numbers are the backend's, read from the capability, so the sentence and the refusal can never disagree. `data-state` is `met` or `unmet`, which is what a skin ticks off. A rule the backend does not have is absent: a "hard to guess" line over a backend that accepts anything would be a promise nobody keeps.

Capability: methods: password

Required scope

PasswordRequirement must be inside <SignIn.Root> or <SignUp.Root>.

Props

3 documented props · Native <li> attributes supported

SignIn.PasswordRequirement props
PropTypeDefault
ruleRequired
"length" | "strength"—
children

Content rendered inside this part.

ReactNode | ((state: State) => 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
  • Requirement from @slices.sh/auth/headless/password-settings
  • SignIn.PasswordRequirement from @slices.sh/auth/headless/sign-in
  • SignIn.PasswordRequirement from @slices.sh/auth/headless/passkeys
  • SignIn.PasswordRequirement from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/sign-in/password-requirement.tsx

SignIn.BackToSignInaction · <button>

Leaves the reset screens. It drops the token as well as the intent, which is what takes a live credential out of the address bar once it has been spent, and the code challenge with it: coming back to a half-typed code for a reset nobody is doing any more is a screen nobody asked for.

Capability: password-reset

Required scope

SignIn.BackToSignIn must be inside <SignIn.Root>.

Props

2 documented props · Native <button> attributes supported

SignIn.BackToSignIn 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
  • SignIn.BackToSignIn from @slices.sh/auth/headless/sign-in
  • SignIn.BackToSignIn from @slices.sh/auth/headless/passkeys
  • SignIn.BackToSignIn from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/sign-in/back-to-sign-in.tsx

SignIn.ForgotPasswordaction · <button>

Asks for the reset screen. Absent unless the backend can actually put a link in front of somebody: a "Forgot password?" that leads nowhere is worse than none at all. `stage="identify"` is for the link under the email field of an identifier-first card: it is there only while that step is drawn. Where the email and the password stand together there is no such step, and the password field's own link is the only one.

Capability: password-reset

Required scope

SignIn.ForgotPassword must be inside <SignIn.Root>.

Props

3 documented props · Native <button> attributes supported

SignIn.ForgotPassword props
PropTypeDefault
stage
"identify"—
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
  • SignIn.ForgotPassword from @slices.sh/auth/headless/sign-in
  • SignIn.ForgotPassword from @slices.sh/auth/headless/passkeys
  • SignIn.ForgotPassword from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/sign-in/forgot-password.tsx

SignIn.RequestResetaction · <button>

ForgotPassword opens the reset steps, and nothing unless the backend can deliver a link or a code. ResetMethod gates on method="link" | "code". RequestReset sends it with { pending, method }; ResetSent says it went, the same for any address. ResetDestination, ResetCodeInput and ResendResetCode are the code variant. ResetUnused gates while a link is unspent; NewPassword and SubmitReset set the password; ResetDone follows. BackToSignIn leaves and drops the token from the address bar.

Capability: password-reset

Required scope

SignIn.RequestReset must be inside <SignIn.Root>.

Props

2 documented props · Native <button> attributes supported

SignIn.RequestReset props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { pending: boolean; method: "link" | "code" }) => 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
  • SignIn.RequestReset from @slices.sh/auth/headless/sign-in
  • SignIn.RequestReset from @slices.sh/auth/headless/passkeys
  • SignIn.RequestReset from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/sign-in/request-reset.tsx

SignIn.ResetMethodgate

How this install replaces a forgotten password: a link to the inbox, or a code to type. A gate, because the two are different screens rather than one screen with a different sentence — a link is followed away and comes back with a token, a code is typed here — and only the backend knows which it can deliver.

Capability: password-reset

Required scope

SignIn.ResetMethod must be inside <SignIn.Root>.

Props

2 documented props

SignIn.ResetMethod props
PropTypeDefault
methodRequired
"link" | "code"—
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
  • SignIn.ResetMethod from @slices.sh/auth/headless/sign-in
  • SignIn.ResetMethod from @slices.sh/auth/headless/passkeys
  • SignIn.ResetMethod from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/sign-in/reset-method.tsx

SignIn.ResetSentgate

ForgotPassword opens the reset steps, and nothing unless the backend can deliver a link or a code. ResetMethod gates on method="link" | "code". RequestReset sends it with { pending, method }; ResetSent says it went, the same for any address. ResetDestination, ResetCodeInput and ResendResetCode are the code variant. ResetUnused gates while a link is unspent; NewPassword and SubmitReset set the password; ResetDone follows. BackToSignIn leaves and drops the token from the address bar.

Capability: password-reset

Required scope

SignIn.ResetSent must be inside <SignIn.Root>.

Props

1 documented props

SignIn.ResetSent props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { delivery: "mail" | "terminal" | "log" }) => ReactNode)—

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

Alternate public imports
  • SignIn.ResetSent from @slices.sh/auth/headless/sign-in
  • SignIn.ResetSent from @slices.sh/auth/headless/passkeys
  • SignIn.ResetSent from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/sign-in/reset-sent.tsx

SignIn.ResetDestinationcontent

Where the reset code went, as the backend spells it back: often masked, never guessed.

Capability: password-reset

Required scope

SignIn.ResetDestination must be inside <SignIn.Root>.

Props

2 documented props

SignIn.ResetDestination 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
  • SignIn.ResetDestination from @slices.sh/auth/headless/sign-in
  • SignIn.ResetDestination from @slices.sh/auth/headless/passkeys
  • SignIn.ResetDestination from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/sign-in/reset-destination.tsx

SignIn.ResetCodeInputinput · <input>

The code that proves the address a reset was asked for. The same six digits as a sign-in code and a different thing entirely: a right one answers a token that sets a password, and signs nobody in. Checked the moment its last digit lands, so there is no button beside it.

Capability: password-reset

Required scope

SignIn.ResetCodeInput must be inside <SignIn.Root>.

Props

2 documented props · Native <input> attributes supported

SignIn.ResetCodeInput 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
  • SignIn.ResetCodeInput from @slices.sh/auth/headless/sign-in
  • SignIn.ResetCodeInput from @slices.sh/auth/headless/passkeys
  • SignIn.ResetCodeInput from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/sign-in/reset-code-input.tsx

SignIn.ResendResetCodeaction · <button>

ForgotPassword opens the reset steps, and nothing unless the backend can deliver a link or a code. ResetMethod gates on method="link" | "code". RequestReset sends it with { pending, method }; ResetSent says it went, the same for any address. ResetDestination, ResetCodeInput and ResendResetCode are the code variant. ResetUnused gates while a link is unspent; NewPassword and SubmitReset set the password; ResetDone follows. BackToSignIn leaves and drops the token from the address bar.

Capability: password-reset

Required scope

SignIn.ResendResetCode must be inside <SignIn.Root>.

Props

2 documented props · Native <button> attributes supported

SignIn.ResendResetCode props
PropTypeDefault
children

Content rendered inside this part.

ReactNode | ((state: { seconds: number; pending: boolean; exhausted: 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
  • SignIn.ResendResetCode from @slices.sh/auth/headless/sign-in
  • SignIn.ResendResetCode from @slices.sh/auth/headless/passkeys
  • SignIn.ResendResetCode from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/sign-in/resend-reset-code.tsx

SignIn.ResetUnusedgate

ForgotPassword opens the reset steps, and nothing unless the backend can deliver a link or a code. ResetMethod gates on method="link" | "code". RequestReset sends it with { pending, method }; ResetSent says it went, the same for any address. ResetDestination, ResetCodeInput and ResendResetCode are the code variant. ResetUnused gates while a link is unspent; NewPassword and SubmitReset set the password; ResetDone follows. BackToSignIn leaves and drops the token from the address bar.

Capability: password-reset

Required scope

SignIn.ResetUnused must be inside <SignIn.Root>.

Props

1 documented props

SignIn.ResetUnused 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
  • SignIn.ResetUnused from @slices.sh/auth/headless/sign-in
  • SignIn.ResetUnused from @slices.sh/auth/headless/passkeys
  • SignIn.ResetUnused from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/sign-in/reset-unused.tsx

SignIn.NewPasswordinput · <input>

The password being set by a reset, which is a new one and is announced as such.

Capability: password-reset

Required scope

SignIn.NewPassword must be inside <SignIn.Root>.

Props

2 documented props · Native <input> attributes supported

SignIn.NewPassword 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
  • SignIn.NewPassword from @slices.sh/auth/headless/sign-in
  • SignIn.NewPassword from @slices.sh/auth/headless/passkeys
  • SignIn.NewPassword from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/sign-in/new-password.tsx

SignIn.SubmitResetaction · <button>

ForgotPassword opens the reset steps, and nothing unless the backend can deliver a link or a code. ResetMethod gates on method="link" | "code". RequestReset sends it with { pending, method }; ResetSent says it went, the same for any address. ResetDestination, ResetCodeInput and ResendResetCode are the code variant. ResetUnused gates while a link is unspent; NewPassword and SubmitReset set the password; ResetDone follows. BackToSignIn leaves and drops the token from the address bar.

Capability: password-reset

Required scope

SignIn.SubmitReset must be inside <SignIn.Root>.

Props

2 documented props · Native <button> attributes supported

SignIn.SubmitReset 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
  • SignIn.SubmitReset from @slices.sh/auth/headless/sign-in
  • SignIn.SubmitReset from @slices.sh/auth/headless/passkeys
  • SignIn.SubmitReset from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/sign-in/submit-reset.tsx

SignIn.ResetDonegate

ForgotPassword opens the reset steps, and nothing unless the backend can deliver a link or a code. ResetMethod gates on method="link" | "code". RequestReset sends it with { pending, method }; ResetSent says it went, the same for any address. ResetDestination, ResetCodeInput and ResendResetCode are the code variant. ResetUnused gates while a link is unspent; NewPassword and SubmitReset set the password; ResetDone follows. BackToSignIn leaves and drops the token from the address bar.

Capability: password-reset

Required scope

SignIn.ResetDone must be inside <SignIn.Root>.

Props

1 documented props

SignIn.ResetDone 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
  • SignIn.ResetDone from @slices.sh/auth/headless/sign-in
  • SignIn.ResetDone from @slices.sh/auth/headless/passkeys
  • SignIn.ResetDone from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/sign-in/reset-done.tsx

SignIn.Rootscope

Step gates on name and accepts render={<MotionPanel />} to animate; without render it is a wrapper-free gate. Status is the one polite live region, mounted for the card's life, that speaks waits and the attempt budget. Viewport is a plain container for animated steps. Error is <div role="alert"> with { code, message }, only for the current step's last failed action. RetryAfter gets { seconds } while a rate limit holds. Restart returns to identify and drops the challenge.

Props

7 documented props

SignIn.Root props
PropTypeDefault
onSignedIn
(destination: string) => void—
channels
readonly OtpChannel[]—
flow
"identifier-first" | "all-methods""identifier-first"
emailPassword
boolean—
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
  • SignIn.Root from @slices.sh/auth/headless/sign-in
  • SignIn.Root from @slices.sh/auth/headless/passkeys
  • SignIn.Root from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/sign-in/root.tsx

SignIn.Viewportcontent · <div>

Step gates on name and accepts render={<MotionPanel />} to animate; without render it is a wrapper-free gate. Status is the one polite live region, mounted for the card's life, that speaks waits and the attempt budget. Viewport is a plain container for animated steps. Error is <div role="alert"> with { code, message }, only for the current step's last failed action. RetryAfter gets { seconds } while a rate limit holds. Restart returns to identify and drops the challenge.

Props

2 documented props · Native <div> attributes supported

SignIn.Viewport 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
  • SignIn.Viewport from @slices.sh/auth/headless/sign-in
  • SignIn.Viewport from @slices.sh/auth/headless/passkeys
  • SignIn.Viewport from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/sign-in/viewport.tsx

SignIn.Stepgate

Step gates on name and accepts render={<MotionPanel />} to animate; without render it is a wrapper-free gate. Status is the one polite live region, mounted for the card's life, that speaks waits and the attempt budget. Viewport is a plain container for animated steps. Error is <div role="alert"> with { code, message }, only for the current step's last failed action. RetryAfter gets { seconds } while a rate limit holds. Restart returns to identify and drops the challenge.

Props

3 documented props

SignIn.Step props
PropTypeDefault
nameRequired
"identify" | "reset-request" | "reset-code" | "reset" | "code" | "two-factor" | "locked" | "expired" | "signed-in"—
children

Content rendered inside this part.

ReactNode—
render
ReactElement—

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

Alternate public imports
  • SignIn.Step from @slices.sh/auth/headless/sign-in
  • SignIn.Step from @slices.sh/auth/headless/passkeys
  • SignIn.Step from @slices.sh/auth/headless/two-factor

Source: @slices.sh/auth/src/headless/sign-in/step.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 →