Components
ElvixSignInForm

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.

A
Sign in to Acme
Pick how you want to continue.
Continue with Google
or
By continuing, you agree to Acme's
v0.10.1
app/sign-in/page.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

PropTypeRequiredDefaultDescription
clientIdstringoptionalfrom <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.
appNamestringoptionalConsole-configuredApp display name. Explicit prop wins over the bootstrap envelope.
logoUrlstring | nulloptionalConsole-configuredLogo URL. logoUrlDark overrides in dark theme. logoNode (ReactNode) wins over both.
brandColorstringoptionalConsole-configuredPrimary brand color for CTAs. onBrandColor sets foreground painted on top.
methodGooglebooleanoptionalConsole-configuredToggle the Google factor. Same for methodEmailOtp, methodPasskey, methodUsername.
googleConfig{ oneTap?, autoSelect?, popup?, fedcm?, hostedDomain? }optionalnonePer-app Google Identity Services config. Falls back to redirect-OAuth when GIS is unavailable.
googleClientIdstringoptionalfrom bootstrapPublic 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.ReactNodeoptionala <ElvixSignInButton>Render-prop for the overlay trigger (modal / drawer only). Receives open; return your own element to launch the overlay.
triggerLabelstringoptionalthe sign-in headingLabel for the default trigger button (modal / drawer only).
triggerPropsPartial<ElvixSignInButtonProps>optionalnoneFull <ElvixSignInButton> surface (variant / size / shape / brandColor …) for the default trigger.
openbooleanoptionaluncontrolledControlled overlay open state. Pair with onOpenChange.
defaultOpenbooleanoptionalfalseInitial open state when uncontrolled.
onOpenChange(open: boolean) => voidoptionalnoneFires whenever the overlay opens or closes.
drawerSide"bottom" | "left" | "right"optional"bottom"Which edge the drawer presentation slides in from (uses vaul).
dismissablebooleanoptionaltrueWhether backdrop-click / Escape (modal) or drag / backdrop (drawer) closes the overlay.
widthnumber | stringoptional420Overlay 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".
showHeaderbooleanoptionaltrueHide the built-in header (logo + heading + subtitle).
framedbooleanoptionaltrueRender the card frame. Set transparentBg to strip bg + border + shadow.
transparentBgbooleanoptionalfalseStrip card bg + border + shadow so the form blends into its host.
belowHeadingReact.ReactNodeoptionalnoneSlot rendered under the subtitle. For gate-state badges ("Private beta").
belowMethodsReact.ReactNodeoptionalnoneSlot rendered under the methods, above the "Secured by elvix" chip.
websiteUrlstring | nulloptionalConsole-configuredIf set, logo becomes a link to this URL.
privacyPolicyUrlstring | nulloptionalConsole-configuredFooter privacy link. termsOfServiceUrl covers ToS.
redirectAfterSignInstringoptionalbackend value, then "/"Single declarative fallback honoured by EVERY success path (OTP, Google, passkey, onboarding).
onAuthenticated(r: { ok: true; redirect?: string; token?: string }) => voidoptionalnoneFires BEFORE the default redirect on success. When set, the form will NOT navigate; the host owns post-auth routing.
onResult(r: ElvixSignInResult) => voidoptionalnoneTerminal callback for every outcome (success + every error path). Branch on r.ok.

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

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.

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

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.

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.

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.

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