Next.js Discord

Discord Forum

Creating a global alert component

Unanswered
Palomino posted this in #help-forum
Open in Discord
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

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;
  }
}