Creating a global alert component
Unanswered
Palomino posted this in #help-forum
PalominoOP
I want to create an alert component that all routes can use. Only one alert can be displayed at a time and it will always be in the same position regardless of the current route. Additionally, I should be able to close the current alert without preventing future alerts from popping up. What I've done is place the Alert component as a child of the root layout
Instead of using useState and passing the setter to all the components that need it which would turn everything into client components, I decided I will read the error param from the search params. I can close the alert by setting the error param to none.
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={`${inter.className} bg-black text-white`}>
{children}
<Suspense>
<Alert>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth="1.5"
stroke="currentColor"
height="24"
width="24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M6 18 18 6M6 6l12 12"
/>
</svg>
</Alert>
</Suspense>
</body>
</html>
);
}Instead of using useState and passing the setter to all the components that need it which would turn everything into client components, I decided I will read the error param from the search params. I can close the alert by setting the error param to none.
export default function Alert({ children }: { children: React.ReactNode }) {
const searchParams = useSearchParams();
const pathName = usePathname();
const router = useRouter();
const error = searchParams.get('error');
if (error) {
const closeAlert = () => {
const newParams = new URLSearchParams(searchParams);
newParams.delete('error');
router.push(`${pathName}?${newParams.toString()}`);
};
return (
<AlertBody className="bg-red-600" message={error} closeAlert={closeAlert}>
{children}
</AlertBody>
);
}
return;
}3 Replies
PalominoOP
So now whenever I catch an error, I will set the error param to the message I want. The slight problem with this is that whenever I close the alert button, it deletes the error param and makes a request to the backend with new URL. I want to avoid this unnecessary request. Is there a better approach to this?
Alert body component
import { useState } from 'react';
export default function AlertBody({
className,
message,
children,
closeAlert,
}: {
className: string;
message: string;
children: React.ReactNode;
closeAlert?: () => void;
}) {
// const [display, setDisplay] = useState('flex');
return (
<div
className={`${className} absolute bottom-8 left-1/2 flex translate-x-[-50%] gap-x-2 rounded-xl px-4 py-2 text-center align-middle font-medium`}
>
<button onClick={closeAlert}>{children}</button>
{/* <button onClick={() => setDisplay('hidden')}>{children}</button> */}
{message}
</div>
);
}An example of setting the error param when I catch an error
export async function authenticate(formData: FormData) {
try {
await signIn('credentials', formData);
} catch (error) {
if (error instanceof AuthError) {
switch (error.type) {
case 'CredentialsSignin':
redirect('/?error=Invalid credentials. Please try again.');
default:
redirect('/?error=Something went wrong. Please try again.');
}
}
throw error;
}
}