# ElvixSignInForm

> The full branded sign-in surface (card, header, brand wash, every Console-enabled method, Secured-by-elvix badge). Exported as both `ElvixSignInForm` and the short alias `ElvixSignIn` — same component.

<!-- app/sign-in/page.tsx -->
```tsx
"use client";

import { ElvixSignInForm } from "@elvix.is/sdk/react";
import { useRouter } from "next/navigation";

export default function SignInPage() {
  const router = useRouter();
  return (
    <ElvixSignInForm
      clientId={process.env.NEXT_PUBLIC_ELVIX_CLIENT_ID!}
      onResult={(r) => {
        if (!r.ok) return console.warn(r.error, r.message);
        if (r.token) {
          const secure = location.protocol === "https:" ? "; secure" : "";
          document.cookie = `elvix_token=${r.token}; path=/; samesite=lax${secure}`;
        }
        router.push(r.redirect ?? "/app");
      }}
    />
  );
}
```

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `clientId` | `string` | optional | from `<ElvixProvider>` | Application client_id. Picked up from context when omitted. |
| `mode` | `"interactive" \| "preview"` | optional | `"interactive"` | `preview` paints every CTA without hitting auth endpoints. Console live-preview uses this. |
| `intent` | `"app" \| "console"` | optional | `"app"` | Routes auth requests for the host context. |
| `appName` | `string` | optional | Console-configured | App display name. Explicit prop wins over the bootstrap envelope. |
| `logoUrl` | `string \| null` | optional | Console-configured | Logo URL. `logoUrlDark` overrides in dark theme. `logoNode` (ReactNode) wins over both. |
| `brandColor` | `string` | optional | Console-configured | Primary brand color for CTAs. `onBrandColor` sets foreground painted on top. |
| `methodGoogle` | `boolean` | optional | Console-configured | Toggle the Google factor. Same for `methodEmailOtp`, `methodPasskey`, `methodUsername`. |
| `googleConfig` | `{ oneTap?, autoSelect?, popup?, fedcm?, hostedDomain? }` | optional | none | Per-app Google Identity Services config. Falls back to redirect-OAuth when GIS is unavailable. |
| `googleClientId` | `string` | optional | from bootstrap | Public Google OAuth client id for the GIS personalized button. |
| `layout` | `"centered" \| "split" \| "stacked"` | optional | `"centered"` | Visual layout variant. |
| `socialLayout` | `"stacked" \| "grid"` | optional | `"stacked"` | Social button arrangement. |
| `presentation` | `"card" \| "drawer" \| "modal"` | optional | `"card"` | Surrounding chrome. `card` renders the form inline. `modal` / `drawer` render an auto-trigger button that opens the form in an overlay and auto-closes on success. Console can preset this via the app's `presentation` setting. |
| `trigger` | `(open: () => void) => React.ReactNode` | optional | a `<ElvixSignInButton>` | Render-prop for the overlay trigger (modal / drawer only). Receives `open`; return your own element to launch the overlay. |
| `triggerLabel` | `string` | optional | the sign-in heading | Label for the default trigger button (modal / drawer only). |
| `triggerProps` | `Partial<ElvixSignInButtonProps>` | optional | none | Full `<ElvixSignInButton>` surface (variant / size / shape / brandColor …) for the default trigger. |
| `open` | `boolean` | optional | uncontrolled | Controlled overlay open state. Pair with `onOpenChange`. |
| `defaultOpen` | `boolean` | optional | `false` | Initial open state when uncontrolled. |
| `onOpenChange` | `(open: boolean) => void` | optional | none | Fires whenever the overlay opens or closes. |
| `drawerSide` | `"bottom" \| "left" \| "right"` | optional | `"bottom"` | Which edge the `drawer` presentation slides in from (uses `vaul`). |
| `dismissable` | `boolean` | optional | `true` | Whether backdrop-click / Escape (modal) or drag / backdrop (drawer) closes the overlay. |
| `width` | `number \| string` | optional | `420` | Overlay panel / drawer-sheet width. Pin to your container width so a bottom drawer fills it edge-to-edge. |
| `theme` | `"light" \| "dark" \| "auto"` | optional | `"light"` | Theme override for the form's surface tokens. `auto` inherits from host. |
| `signInVerb` | `"signin" \| "login"` | optional | `"signin"` | CTA verb. SaaS defaults to "Sign in"; banks/B2B legacy prefer "Log in". |
| `showHeader` | `boolean` | optional | `true` | Hide the built-in header (logo + heading + subtitle). |
| `framed` | `boolean` | optional | `true` | Render the card frame. Set `transparentBg` to strip bg + border + shadow. |
| `transparentBg` | `boolean` | optional | `false` | Strip card bg + border + shadow so the form blends into its host. |
| `belowHeading` | `React.ReactNode` | optional | none | Slot rendered under the subtitle. For gate-state badges ("Private beta"). |
| `belowMethods` | `React.ReactNode` | optional | none | Slot rendered under the methods, above the "Secured by elvix" chip. |
| `websiteUrl` | `string \| null` | optional | Console-configured | If set, logo becomes a link to this URL. |
| `privacyPolicyUrl` | `string \| null` | optional | Console-configured | Footer privacy link. `termsOfServiceUrl` covers ToS. |
| `redirectAfterSignIn` | `string` | optional | backend value, then `"/"` | Single declarative fallback honoured by EVERY success path (OTP, Google, passkey, onboarding). |
| `onAuthenticated` | `(r: { ok: true; redirect?: string; token?: string }) => void` | optional | none | Fires BEFORE the default redirect on success. When set, the form will NOT navigate; the host owns post-auth routing. |
| `onResult` | `(r: ElvixSignInResult) => void` | optional | none | Terminal callback for every outcome (success + every error path). Branch on `r.ok`. |

> **Note:** `ElvixSignIn` is a re-export of `ElvixSignInForm` — same component, shorter name. Import whichever reads better; there is no longer a separate lower-level component.

> **Note:** Open the form in an overlay instead of inline with `presentation="modal"` or `presentation="drawer"`. The SDK auto-renders a trigger button (`<ElvixSignInForm presentation="modal" triggerLabel="Sign in" />`); customise it with `triggerProps` (full `<ElvixSignInButton>` surface) or replace it entirely with the `trigger` render-prop. The overlay closes on success, on backdrop-click / Escape (modal), or drag / backdrop (drawer) unless `dismissable={false}`. The drawer uses `vaul` (the same primitive shadcn/ui Drawer wraps), so it drops into a shadcn host unchanged. Pin `width` to your container so a bottom drawer fills it edge-to-edge.

> **Warning:** `onAuthenticated` suppresses the default redirect; `onResult` does not. If you set `onAuthenticated`, the host owns navigation. `onResult` fires on every outcome regardless, including errors.

> **Note:** After a Google round-trip (`#elvix_token=...` in the URL fragment), the form fires `onResult` AND `onAuthenticated` with the same `{ ok: true, token, redirect }` payload that an in-frame OTP / passkey success carries. Your handler runs unchanged. **Pin `^0.7.6`**: 0.7.5 only fired `onResult` on the return path, so hosts wired to `onAuthenticated` (server-action token exchange before navigating) sat on `/sign-in` after Google instead of advancing.

> **Note:** **Authenticating pane (0.7.12+).** Between OTP / passkey verification and the host's `onResult` callback firing, the form switches to a dedicated `step="authenticating"` state rendering a `Loader2` + `Signing you in…` + `Hold on a second, taking you to <appName>` line. Removes the stale-form half-second between the last keypress and the host's redirect.

> **Note:** **Onboarding resume after Google return (0.7.12+).** When elvix's Google callback encodes a remaining onboarding gate in the URL fragment as `#elvix_landing=<base64-json>`, the form drains it on mount and renders the username / passkey / recover gate inline. Hosts that don't mount the form on the return page get the legacy behaviour — token in fragment, host navigates to wherever it had planned. Both paths are backwards-compatible.

> **Note:** **Try-inline-first passkey (0.7.12+).** Cross-origin hosts now run `navigator.credentials.get()` inline FIRST (browsers honour elvix's `/.well-known/webauthn` Related Origin Requests manifest). If the browser still rejects with rp.id / SecurityError (older Firefox, older Safari), the form falls back to the hosted ceremony page on elvix.is via the redirect-OAuth pattern. Best UX for modern browsers, no regression for older ones.


## Related

- [ElvixSignInButton](/docs/components/elvix-sign-in-button.md): Drop-in 'Sign in with elvix' button. Four variants by three sizes by four shapes. The cheapest entry point: one line and you have a working sign-in flow.
- [ElvixSignOutButton](/docs/components/elvix-sign-out-button.md): Symmetric counterpart of `<ElvixSignInButton>`. One click invalidates the elvix session server-side, clears the SDK's local token, drops the `elvix_token` cookie, then navigates.
