AuthGuard Pattern: Usage, Implementation, and Best Practices
This document explains the consolidated AuthGuard pattern for authentication in your app, including:
- Why this pattern is recommended
- How to use the AuthGuard component and hooks
- Implementation details
- Example usage for layouts, pages, and conditional UI
- Industry standards and rationale
Why Use a Centralized AuthGuard?
- Single source of truth: All protected route logic (redirect, fallback, loading, inline sign-in) is in one place.
- DRY: No need to repeat guard logic on every page/component.
- Flexible: Supports redirect, fallback, or inline sign-in with simple props.
- Clear separation: Use the AuthGuard component for protected routes, and the
useAuthGuardhook for conditional UI.
AuthGuard Component: Implementation
import { useAuthQuery } from "@/lib/hooks/useAuthQuery"
import { useRouter } from "next/navigation"
import { useEffect } from "react"
import { Loader2 } from "lucide-react"
import SignIn from "./SignIn"
interface AuthGuardProps {
children: React.ReactNode
fallback?: React.ReactNode
redirectTo?: string
showSignIn?: boolean
loadingComponent?: React.ReactNode
}
export default function AuthGuard({
children,
fallback,
redirectTo = "/signin",
showSignIn = false,
loadingComponent,
}: AuthGuardProps) {
const { user, isLoading, isInitialized } = useAuthQuery()
const router = useRouter()
useEffect(() => {
if (isInitialized && !isLoading && !user && !fallback && !showSignIn) {
router.replace(redirectTo)
}
}, [user, isLoading, isInitialized, router, redirectTo, fallback, showSignIn])
if (!isInitialized || isLoading) {
if (loadingComponent) return <>{loadingComponent}</>
return (
<div className="flex items-center justify-center min-h-[300px]">
<div className="flex flex-col items-center space-y-4">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
<p className="text-muted-foreground">Loading...</p>
</div>
</div>
)
}
if (user) return <>{children}</>
if (fallback) return <>{fallback}</>
if (showSignIn) return <SignIn />
return null
}
How to Use
1. Protect a Layout or Page
// app/profile/layout.tsx
import AuthGuard from "@/components/auth/AuthGuard"
export default function ProfileLayout({ children }) {
return <AuthGuard>{children}</AuthGuard>
}
2. Show Inline Sign-In Instead of Redirect
<AuthGuard showSignIn>{children}</AuthGuard>
3. Show a Fallback Component
<AuthGuard fallback={<div>Access denied</div>}>{children}</AuthGuard>
4. Custom Loading UI
<AuthGuard loadingComponent={<MySpinner />}>{children}</AuthGuard>
Conditional UI (No Redirect)
Use the useAuthGuard hook for conditional rendering in any component:
import { useAuthGuard } from "@/lib/hooks/useAuthGuard"
const { isAuthenticated, user } = useAuthGuard()
if (isAuthenticated) {
// Show user content
}
Industry Standards & Rationale
- Centralized guard components are the standard in Next.js, React Router, and other frameworks for DRY, maintainable auth.
- Hooks for state, components for side effects: Use hooks for state, components for redirect/fallback logic.
- Flexible props: Allow for fallback, inline sign-in, and custom loading for better UX.
- SSR/SSG: For server-rendered routes, check session on the server and use the same guard pattern on the client.
Summary Checklist
- Use
AuthGuardin all protected layouts/pages - Use
useAuthGuardfor conditional UI (no redirect) - Remove duplicate/legacy guard hooks
- Pass
fallback,showSignIn, orloadingComponentas needed - Redirects and toasts are handled in one place
This pattern keeps your authentication logic robust, DRY, and easy to reason about.