Components/Auth Components

Auth Components

Ready-to-use authentication components for sign-in, user menus, and session management

NowStack comes with all the components you need to easily manage authentication. They are located in src/features/auth/.

SignInButton

Client component that displays a Sign in button. It opens the sign-in modal — it sets the modal: "signin" search param and masks the URL to /auth/signin (unmaskOnReload), so the visitor can authenticate without a full-page redirect. See Sign-in & sign-up modals below.

Props: VariantProps<typeof buttonVariants> (accepts button variants like size, variant, etc.)

import { SignInButton } from "@/features/auth/sign-in-button";

export default function Home() {
  return (
    <div>
      <SignInButton size="lg" variant="default" />
    </div>
  );
}

LoggedInButton

Client component that displays the user's avatar with a dropdown menu. When the user clicks on it, they see the user's name/email, an Admin link (admins only), a Theme switcher (Dark / Light / System), and Logout.

Props:

{
  user: {
    name?: string | null;
    email?: string | null;
    image?: string | null;
  }
}
import { LoggedInButton } from "@/features/auth/sign-in-button";

export default function Home() {
  const user = await getUser();
  if (!user) return null;

  return (
    <div>
      <LoggedInButton user={user} />
    </div>
  );
}

AuthButton (Server Side)

Server component that automatically displays LoggedInButton or SignInButton based on user authentication status.

Props: None

Features:

  • Uses Suspense with Skeleton fallback
  • Fetches user server-side
  • Ideal for layouts and server pages
import { AuthButton } from "@/features/auth/auth-button";

export default function Header() {
  return (
    <div>
      <AuthButton />
    </div>
  );
}

AuthButtonClient (Client Side)

Client component with the same behavior as AuthButton. It automatically displays LoggedInButton or SignInButton based on the user session.

Props: None

Features:

  • Uses the useSession hook client-side
  • Use when you need a client component
import { AuthButtonClient } from "@/features/auth/auth-button-client";

export default function ClientHeader() {
  return (
    <div>
      <AuthButtonClient />
    </div>
  );
}

Logout

Logout is handled via the dropdown menu in LoggedInButton. UserDropdownLogout signs the user out and then does a full reload to the marketing home / (in production, https://trueroyalties.com/) — not to /auth/signin. Because signOut already clears the auth cookie, the server renders the public landing directly, with no auth-page flash.

To avoid the protected /app page briefly throwing "You need to be signed in" while the session tears down, a global LogoutOverlay (backed by a small Zustand store, logout-store.ts, and mounted in __root.tsx above every route error boundary) raises a full-screen curtain the instant Logout is clicked and keeps it up until the reload lands.

Sign-in & sign-up modals

Sign-in and sign-up are also available as intercepted modals so visitors can authenticate without leaving the page they're on.

  • SignInDialog and SignUpDialog (src/features/auth/) are mounted once, globally, in src/routes/__root.tsx.
  • Any <Link> opens them by setting the modal search param and masking the URL:
    • sign-in → search: { modal: "signin" }, mask={{ to: "/auth/signin" }}
    • sign-up → search: { modal: "signup" }, mask={{ to: "/auth/signup" }}
  • On reload the mask resolves to the real /auth/signin or /auth/signup page.
  • The switch links swap modal-to-modal: "Sign up" inside the sign-in modal opens the sign-up modal, and vice-versa (SignInProviders accepts a variant="modal" for this). The standalone /auth/signin page keeps its page-to-page link (variant="page", the default).

SignInButton and the landing "Start free" CTAs use this modal pattern.

Email verification & WelcomeCelebration

After sign-up, email/password accounts land on /auth/verify to enter the emailed 6-digit code. On success, instead of a toast, the page shows a full-screen WelcomeCelebration (src/features/auth/welcome-celebration.tsx): a rainbow canvas-confetti burst plus a large spring-animated "Welcome" (via motion). It holds briefly, fades out, then calls onComplete to navigate into the app.

Props:

{
  title?: string;        // default "Welcome"
  subtitle?: string;     // default product tagline
  onComplete?: () => void; // fired once, after the fade-out
  durationMs?: number;   // hold before the fade — default 2600
}

OtpPasteButton

Icon-only "paste the code from clipboard" button shared by the sign-in OTP form and the /auth/verify page (src/features/auth/otp-paste-button.tsx). It reads the clipboard, keeps digits only (max 6), and calls onPaste(digits) — the caller fills the boxes and auto-verifies at 6 digits.

import { OtpPasteButton } from "@/features/auth/otp-paste-button";

<OtpPasteButton onPaste={(digits) => setOtp(digits)} />;
Get Organization Member APIContact Components