👁️
1import type { ErrorComponentProps } from "@tanstack/react-router";
2import { Link, useRouter } from "@tanstack/react-router";
3import { AlertTriangle, Check, Copy, Home, RefreshCw } from "lucide-react";
4import { useState } from "react";
5
6export function RouteErrorComponent({
7 error,
8 info,
9 reset,
10}: ErrorComponentProps) {
11 const router = useRouter();
12 const [copied, setCopied] = useState(false);
13
14 const copyErrorDetails = async () => {
15 const details = [
16 `Error: ${error.message}`,
17 `URL: ${window.location.href}`,
18 `Time: ${new Date().toISOString()}`,
19 `User Agent: ${navigator.userAgent}`,
20 "",
21 "Stack Trace:",
22 error.stack || "(no stack trace)",
23 "",
24 ...(info?.componentStack
25 ? ["Component Stack:", info.componentStack]
26 : []),
27 ].join("\n");
28
29 await navigator.clipboard.writeText(details);
30 setCopied(true);
31 setTimeout(() => setCopied(false), 2000);
32 };
33
34 return (
35 <div className="min-h-screen bg-white dark:bg-zinc-900 flex items-center justify-center p-6">
36 <div className="max-w-4xl w-full">
37 <div className="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-6">
38 <div className="flex items-start gap-4">
39 <div className="p-2 bg-red-100 dark:bg-red-900/40 rounded-full">
40 <AlertTriangle className="w-6 h-6 text-red-600 dark:text-red-400" />
41 </div>
42 <div className="flex-1 min-w-0">
43 <h1 className="text-xl font-bold text-red-900 dark:text-red-100 mb-2">
44 Something went wrong
45 </h1>
46 <p className="text-red-700 dark:text-red-300 mb-4">
47 {error.message || "An unexpected error occurred"}
48 </p>
49
50 <div className="flex flex-wrap gap-3 mb-4">
51 <button
52 type="button"
53 onClick={() => {
54 reset();
55 router.invalidate();
56 }}
57 className="inline-flex items-center gap-2 px-4 py-2 bg-red-600 hover:bg-red-700 text-white font-medium rounded-lg transition-colors"
58 >
59 <RefreshCw className="w-4 h-4" />
60 Try again
61 </button>
62 <button
63 type="button"
64 onClick={copyErrorDetails}
65 className="inline-flex items-center gap-2 px-4 py-2 bg-gray-200 dark:bg-zinc-700 hover:bg-gray-300 dark:hover:bg-zinc-600 text-gray-900 dark:text-white font-medium rounded-lg transition-colors"
66 >
67 {copied ? (
68 <Check className="w-4 h-4" />
69 ) : (
70 <Copy className="w-4 h-4" />
71 )}
72 {copied ? "Copied!" : "Copy details"}
73 </button>
74 <Link
75 to="/"
76 className="inline-flex items-center gap-2 px-4 py-2 bg-gray-200 dark:bg-zinc-700 hover:bg-gray-300 dark:hover:bg-zinc-600 text-gray-900 dark:text-white font-medium rounded-lg transition-colors"
77 >
78 <Home className="w-4 h-4" />
79 Go home
80 </Link>
81 </div>
82
83 {import.meta.env.DEV && (
84 <div className="space-y-2">
85 <pre className="text-xs bg-red-100 dark:bg-red-900/30 p-3 rounded overflow-x-auto text-red-800 dark:text-red-200 whitespace-pre-wrap break-words max-h-64 overflow-y-auto">
86 {error.stack || error.message}
87 </pre>
88 {info?.componentStack && (
89 <pre className="text-xs bg-red-100 dark:bg-red-900/30 p-3 rounded overflow-x-auto text-red-800 dark:text-red-200 whitespace-pre-wrap break-words max-h-48 overflow-y-auto">
90 {info.componentStack}
91 </pre>
92 )}
93 </div>
94 )}
95 </div>
96 </div>
97 </div>
98 </div>
99 </div>
100 );
101}