Elements

Components / Application

Authentication

Login and join flows for Instant Strata. The layout follows the two-column shadcn signup-02 pattern: form on the left, cover image with brand detail on the right. Sign-in options are ordered social first, magic link second, email and password third. These components are UI only; wire callbacks to Supabase Auth in production. Email copy for each Supabase template is in Authentication emails.

Installation

bash
npx shadcn add https://raw.githubusercontent.com/levi-putna/elements/main/registry/auth/registry.json

Installs auth-layout.tsx and auth-form.tsx. Requires logo, button, field, input, input-otp, separator, and utils from this registry.

Flows

Three sign-in paths share one layout. Social and magic link sit above the divider; email and password is the standard fallback below it.

Social OAuth

Google and Microsoft sit side by side in a two-column row on wider viewports. Wire to Supabase signInWithOAuth in production.

Magic link

A third outline button under social options opens a dedicated email step. After submit, the user sees confirmation copy and a 6-digit PIN field.

Email and password

The default path below the divider. Login uses email and password. Join adds first and last name in a two-column row above email and password.

Brand panel

The right column shows the building cover image with a forest overlay and centred Instant Strata value props. Uses assetPath() so images resolve under a Next.js basePath.

Login

Email and password below the divider. Click "Continue with magic link" to try the alternate flow, or toggle to join via the footer link.

Welcome back

Sign in to your Instant Strata workspace.

Or continue with email

New to Instant Strata?

By continuing, you agree to our Terms of service and Privacy statement.

Join

First and last name share a row. Email and password follow full width.

Create your account

Join Instant Strata to manage schemes, lots, and owner communication in one place.

Or continue with email

Use at least 8 characters.

Already have an account?

By continuing, you agree to our Terms of service and Privacy statement.

Magic link sent

Final step after magic link submit. Explains that a link and PIN were sent, then collects the 6-digit code.

Check your email

We sent an email to manager@instantstrata.com.au with a sign-in link and a one-time PIN. Open the link on this device, or enter the PIN below.

Enter the 6-digit code from your email. Codes expire after 10 minutes.

Wrong address?

Supabase integration (production)

These components do not call Supabase directly. When you wire the production app, map callbacks as follows:

  • onGoogleSignIn and onMicrosoftSignIn: supabase.auth.signInWithOAuth.
  • onEmailPasswordSubmit: signInWithPassword for login, signUp for join (pass first and last name as user metadata).
  • onMagicLinkSubmit: supabase.auth.signInWithOtp. Advance to magic-link-sent on success.
  • onPinSubmit: supabase.auth.verifyOtp with type: "email".
tsx
/*
 * Auth form steps
 *
 * 1. credentials (default)
 *    Social buttons (Google, Microsoft), magic link option, then email/password.
 *    Join mode adds first and last name in a two-column row.
 *
 * 2. magic-link-email
 *    Email-only step after "Continue with magic link".
 *
 * 3. magic-link-sent
 *    Confirmation copy plus InputOTP (3 + 3 grouping).
 *
 * Wire onEmailPasswordSubmit to supabase.auth.signInWithPassword / signUp.
 * Wire onMagicLinkSubmit to supabase.auth.signInWithOtp({ email }).
 * Wire onPinSubmit to supabase.auth.verifyOtp({ email, token: pin, type: "email" }).
 */

Cover image and basePath

The default cover image uses assetPath() from lib/utils. Apps served under a Next.js basePath should set NEXT_PUBLIC_BASE_PATH so public assets resolve correctly. Without it, paths like /img/building/1.webp are used as-is.

Usage

Login page

tsx
import { AuthForm } from "@/components/ui/auth-form"
import { AuthLayout } from "@/components/ui/auth-layout"

export default function LoginPage() {
  return (
    <AuthLayout logoHref="/">
      <AuthForm
        mode="login"
        onGoogleSignIn={() => signInWithGoogle()}
        onMicrosoftSignIn={() => signInWithMicrosoft()}
        onEmailPasswordSubmit={({ email, password }) =>
          signInWithPassword({ email, password })
        }
        onMagicLinkSubmit={({ email }) => sendMagicLink({ email })}
        onPinSubmit={({ email, pin }) => verifyMagicLinkPin({ email, pin })}
      />
    </AuthLayout>
  )
}

Join page

tsx
import { AuthForm } from "@/components/ui/auth-form"
import { AuthLayout } from "@/components/ui/auth-layout"

export default function JoinPage() {
  return (
    <AuthLayout logoHref="/">
      <AuthForm
        mode="join"
        onGoogleSignIn={() => signInWithGoogle()}
        onMicrosoftSignIn={() => signInWithMicrosoft()}
        onEmailPasswordSubmit={({ email, password, firstName, lastName }) =>
          signUpWithPassword({ email, password, firstName, lastName })
        }
        onMagicLinkSubmit={({ email }) => sendMagicLink({ email })}
        onPinSubmit={({ email, pin }) => verifyMagicLinkPin({ email, pin })}
      />
    </AuthLayout>
  )
}

AuthLayout props

PropTypeDefaultDescription
childrenReactNode-Form column content, typically AuthForm.
logoHrefstring"/"Destination for the Instant Strata logo link.
imageSrcstring-Cover image for the right column. Defaults to the building photo via assetPath().
imageAltstring-Accessible description for the cover image.
brandContentReactNode-Optional override for the centred brand copy over the image.

AuthForm props

PropTypeDefaultDescription
mode"login" | "join""login"Switches heading, fields, and primary button label.
defaultMode"login" | "join""login"Initial mode when uncontrolled.
step"credentials" | "magic-link-email" | "magic-link-sent"-Controlled step for the active auth view.
defaultStep"credentials" | "magic-link-email" | "magic-link-sent""credentials"Initial step when uncontrolled.
emailstring-Email value (controlled).
defaultEmailstring-Initial email value when uncontrolled.
pinstring-Controlled OTP value for the PIN step.
onGoogleSignIn() => void-Called when the user chooses Google.
onMicrosoftSignIn() => void-Called when the user chooses Microsoft.
onEmailPasswordSubmit({ email, password, firstName?, lastName? }) => void-Called when the user submits the email/password form.
onMagicLinkSubmit({ email: string }) => void-Called when the user submits their email for a magic link.
onPinSubmit({ email: string; pin: string }) => void-Called when the user submits the one-time PIN.
onModeChange({ mode: AuthMode }) => void-Called when the user toggles between login and join.
onStepChange({ step: AuthFormStep }) => void-Called when the form step changes.
passwordLoadingbooleanfalseLoading state on the email/password submit button.
magicLinkLoadingbooleanfalseLoading state on the magic-link submit button.
pinLoadingbooleanfalseLoading state on the PIN verify button.