From 8a444a51a138a3a370892e7a3b75a6dbc9764739 Mon Sep 17 00:00:00 2001 From: Chris Parsons <438676+chrisparsons83@users.noreply.github.com> Date: Sat, 21 Feb 2026 23:47:42 -0800 Subject: [PATCH] Improve error boundary UI with status-specific error pages (#21) * Improve error pages with styled, status-aware UI Replace the bare-bones error boundary with a proper error page that: - Renders a minimal Brackt header so users can navigate home - Shows distinct messages and icons for 401, 403, 404, 500+ errors - Displays a "Sign in from the home page" hint on 401 so logged-out users know how to recover - Hides internal details in production (stack traces dev-only) - Uses existing dark theme Tailwind classes for visual consistency https://claude.ai/code/session_011jv8desa5vhSkjZHSjWZiV * Address code review feedback on error pages - Move statusIcon out of ErrorBoundary render as a plain module-level function to avoid React re-creating a component type on every render - Collapse 401 action into a single descriptive button label ("Go to Home Page to Sign In") instead of a fragmented button + hint - Fix details/summary border radius collision by using overflow-hidden on the container and removing rounded-md from the summary element; separate the pre block with a border-t instead https://claude.ai/code/session_011jv8desa5vhSkjZHSjWZiV --------- Co-authored-by: Claude --- app/root.tsx | 106 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 86 insertions(+), 20 deletions(-) diff --git a/app/root.tsx b/app/root.tsx index 4c430d6..ae5daa3 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -16,6 +16,7 @@ import { ClerkProvider } from "@clerk/react-router"; import { dark } from "@clerk/themes"; import { Toaster } from "~/components/ui/sonner"; import { isUserAdminByClerkId } from "~/models/user"; +import { AlertCircle, FileQuestion, Lock, ShieldOff, ServerCrash } from "lucide-react"; export const middleware: Route.MiddlewareFunction[] = [clerkMiddleware()]; @@ -83,30 +84,95 @@ export default function App({ loaderData }: Route.ComponentProps) { } export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) { - let message = "Oops!"; - let details = "An unexpected error occurred."; + let status = 500; + let title = "Something Went Wrong"; + let message = "An unexpected error occurred. Please try again later."; let stack: string | undefined; - + let showSignInHint = false; if (isRouteErrorResponse(error)) { - message = error.status === 404 ? "404" : "Error"; - details = - error.status === 404 - ? "The requested page could not be found." - : error.statusText || details; - } else if (import.meta.env.DEV && error && error instanceof Error) { - details = error.message; - stack = error.stack; + status = error.status; + switch (error.status) { + case 401: + title = "Sign In Required"; + message = "You need to be signed in to access this page."; + showSignInHint = true; + break; + case 403: + title = "Access Denied"; + message = "You don't have permission to view this page."; + break; + case 404: + title = "Page Not Found"; + message = "The page you're looking for doesn't exist or has been moved."; + break; + default: + title = `Error ${error.status}`; + message = + typeof error.data === "string" && error.data + ? error.data + : error.statusText || "Something went wrong."; + } + } else if (error instanceof Error) { + title = "Unexpected Error"; + message = import.meta.env.DEV ? error.message : "An unexpected error occurred. Please try again later."; + stack = import.meta.env.DEV ? error.stack : undefined; } return ( -
-

{message}

-

{details}

- {stack && ( -
-          {stack}
-        
- )} -
+
+
+
+ + Brackt + +
+
+ +
+
+
+ {status} +
+ +
+ {statusIcon(status)} +
+ +
+

{title}

+

{message}

+
+ +
+ + {showSignInHint ? "Go to Home Page to Sign In" : "Go Home"} + +
+ + {stack && ( +
+ + Stack trace (development only) + +
+                {stack}
+              
+
+ )} +
+
+
); } + +function statusIcon(status: number) { + const cls = "h-14 w-14 text-muted-foreground"; + if (status === 401) return ; + if (status === 403) return ; + if (status === 404) return ; + if (status >= 500) return ; + return ; +}