> ## Documentation Index
> Fetch the complete documentation index at: https://docs.scute.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Auth UI

> Drop-in auth for React apps

# Auth UI

Add auth to your app in 2 minutes. Magic links, passkeys, OTP — all configured from your dashboard.

## Setup

```bash theme={null}
npx create-scute-app
```

Or manually:

```bash theme={null}
pnpm add @scute/js-core @scute/react-hooks @scute/auth-ui-react @scute/nextjs-handlers
```

```bash theme={null}
# .env.local
NEXT_PUBLIC_SCUTE_APP_ID=your-app-id
NEXT_PUBLIC_SCUTE_BASE_URL=https://api.scute.io
SCUTE_SECRET=your-app-secret
```

***

## Option 1: Drop-in component

```tsx theme={null}
import { ScuteAuthGate } from "@scute/auth-ui-react";

<ScuteAuthGate>
  <App />
</ScuteAuthGate>
```

Shows login when not authenticated. Renders your app when authenticated. Handles passkey registration, magic links, OTP — everything.

***

## Option 2: Headless hook

Full control over the UI.

```tsx theme={null}
import { useScuteAuthFlow } from "@scute/auth-ui-react";

function Auth() {
  const auth = useScuteAuthFlow();

  if (auth.isAuthenticated) return <App />;

  if (auth.view === "login") {
    return (
      <form onSubmit={(e) => { e.preventDefault(); auth.submitIdentifier(); }}>
        <input
          value={auth.identifier}
          onChange={(e) => auth.setIdentifier(e.target.value)}
          placeholder="you@example.com"
        />
        <button>{auth.submitting ? "..." : "Continue"}</button>
      </form>
    );
  }

  if (auth.view === "magic_pending") {
    return <p>Check your email: {auth.identifier}</p>;
  }

  if (auth.view === "webauthn_register") {
    return (
      <div>
        <p>Register a passkey?</p>
        <button onClick={auth.registerPasskey}>Register</button>
        <button onClick={auth.skipPasskey}>Skip</button>
      </div>
    );
  }

  return <p>Loading...</p>;
}
```

### Views

| View                        | What's happening              |
| --------------------------- | ----------------------------- |
| `loading`                   | SDK initializing              |
| `login`                     | Show email input              |
| `magic_pending`             | Magic link sent               |
| `magic_verifying`           | Processing link from URL      |
| `otp_input`                 | Show code input               |
| `webauthn_verify`           | Passkey prompt active         |
| `webauthn_register`         | Offer passkey registration    |
| `webauthn_register_success` | Registered (auto-transitions) |
| `error`                     | Show `auth.error` + retry     |
| `authenticated`             | Done                          |

### Actions

| Action                      | What it does                       |
| --------------------------- | ---------------------------------- |
| `auth.setIdentifier(email)` | Set email before submit            |
| `auth.submitIdentifier()`   | Send magic link or trigger passkey |
| `auth.submitOtp(code)`      | Verify OTP code                    |
| `auth.registerPasskey()`    | Register passkey on this device    |
| `auth.skipPasskey()`        | Skip registration, sign in         |
| `auth.retry()`              | Back to login                      |
| `auth.signOut()`            | Sign out                           |

***

## Frameworks

**Next.js** — `npx create-scute-app` handles everything.

**Astro** — use as a React island with `client:only="react"`.

**Remix / TanStack Start** — wrap with provider, use components.

The CLI scaffolds the right files for each framework.

***

## Styling

Default component uses `data-scute-*` attributes:

```css theme={null}
[data-scute-view="login"] { }
[data-scute-view="webauthn_register"] { }
[data-scute-input] { }
[data-scute-button="primary"] { }
[data-scute-error] { }
```

Or use the headless hook and bring your own components.
