Remix Toast

repository·main·Indexed 42 days ago

https://github.com/code-forge-io/remix-toast

A server-side toast management library for React Router v7 and Remix.run that handles passing notifications from server actions and loaders to the client via cookies. Version 4.0.0 provides utilities for redirects, data responses, and middleware mode, and is compatible with client-side UI libraries like react-toastify and Sonner.

Tokens
7.4K
Snippets
30
Records
31
Agent score
54%

What's inside Remix Toast

  1. Deploy using Docker

    main

    The template provides optimized Dockerfiles for different package managers. Choose the Dockerfile that matches your package manager (npm, pnpm, or bun) to build your image.

    # For npm
    docker build -t my-app .
    
    # For pnpm
    docker build -f Dockerfile.pnpm -t my-app .
    
    # For bun
    docker build -f Dockerfile.bun -t my-app .
    
    # Run the container
    docker run -p 3000:3000 my-app
  2. Migrate from Remix.run to React Router v7

    main

    If you are upgrading from Remix.run to React Router v7, you must upgrade to remix-toast v2.0.0 or higher. The primary change is that all json utility methods have been renamed to data methods. Redirect methods remain unchanged.

    Example mapping:

    • jsonWithSuccess $\rightarrow$ dataWithSuccess
    • jsonWithError $\rightarrow$ dataWithError
    • etc.
    - import { jsonWithSuccess } from "remix-toast";
    + import { dataWithSuccess } from "remix-toast";
    
     export const action = () => {
    - return jsonWithSuccess({ result: "Data saved successfully" }, "Operation successful! 🎉");
    + return dataWithSuccess({ result: "Data saved successfully" }, "Operation successful! 🎉");
    };
  3. Setup Server-side Toast (Standard Mode)

    main

    In standard mode, you extract the toast from the request object in your loader. Important: You must pass the returned headers back into your data response to ensure the toast cookie is cleared properly after being read.

    import { getToast } from "remix-toast";
    
    export const loader = async ({ request }: LoaderFunctionArgs) => {
      // Extracts the toast from the request
      const { toast, headers } = await getToast(request);
      // Important to pass in the headers so the toast is cleared properly
      return data({ toast }, { headers });
    }
    
    export default function App({ children }: { children: ReactNode }) {
      const { toast } = useLoaderData<typeof loader>();
    
      useEffect(() => {
       if(toast){
        // Call your toast function here
        alert(toast.message);
       }
      }, [toast])
    
      return (
        ...
      );
    }
  4. Setup Server-side Toast with Middleware Mode

    main

    To enable toast functionality globally using middleware mode, add the following to your root.tsx. This approach uses context to manage toasts via getToast and setToast and requires exporting the toastMiddleware.

    import { getToast, setToast, toastMiddleware } from "remix-toast/middleware";
    
    export const loader = async ({ request, context }: Route.LoaderArgs) => {
      // Extracts the toast from the request
      const toast = getToast(context);
      // pass it to the client side
      return { toast }
    }
    
    export const action = async ({ request, context }: Route.ActionArgs) => {
      setToast(context, { message: "hello toast!", type: "success" });
      return null
    }
    
    export default function App({ loaderData }: Route.ComponentProps) {
      const { toast } = loaderData
    
      useEffect(() => {
       if(toast){
        // Call your toast function here
        alert(toast.message);
       }
      }, [toast])
    
      return (
        ...
      );
    }
    
    // Export the middleware to be used in the app
    export const middleware = [toastMiddleware()];
  5. Deploy manually (DIY Deployment)

    main

    If you are deploying a Node application manually, ensure you deploy the output generated by npm run build. The build structure is as follows:

    • build/client/: Static assets
    • build/server/: Server-side code
    ├── package.json
    ├── package-lock.json (or pnpm-lock.yaml, or bun.lockb)
    ├── build/
    │   ├── client/    # Static assets
    │   └── server/    # Server-side code
  6. Important: Await redirect and replace utilities when using throw

    main

    If you use the throw keyword with any redirectWith... or replaceWith... utilities, you must await the function call. These utilities generate a cookie and set it on the response headers; failing to await them will prevent the headers from being set correctly, causing the redirect to fail.

    Correct usage:

    throw await redirectWithToast("/path", { ... });
  7. Integrate with Sonner

    main

    To use sonner for client-side rendering, include the Toaster component in your app and use a useEffect hook to call notify.error() or notify.success() based on the toast.type provided by the server.

    import { data, type LinksFunction, type LoaderFunctionArgs } from "react-router";
    import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration, useLoaderData } from "react-router";
    import { useEffect } from "react";
    import { getToast } from "remix-toast";
    import { Toaster, toast as notify } from "sonner";
    
    export const loader = async ({ request }: LoaderFunctionArgs) => {
      const { toast, headers } = await getToast(request);
      return data({ toast }, { headers });
    };
    
    export default function App() {
      const { toast } = useLoaderData<typeof loader>();
      // Hook to show the toasts
      useEffect(() => {
        if (toast?.type === "error") {
          notify.error(toast.message);
        }
        if (toast?.type === "success") {
          notify.success(toast.message);
        }
      }, [toast]);
    
      return (
        <html lang="en">
          <head>...</head>
          <body
            >
            ...
            <Toaster />
          </body>
        </html>
      );
    }
  8. Integrate with react-toastify

    main

    To use react-toastify for client-side rendering, import the toast styles in your links function, include the ToastContainer in your component tree, and trigger notifications inside a useEffect hook using the toast object from your loader data.

    import { data, type LinksFunction, type LoaderFunctionArgs } from "react-router";
    import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration, useLoaderData } from "react-router";
    import { useEffect } from "react";
    import { getToast } from "remix-toast";
    import { ToastContainer, toast as notify } from "react-toastify";
    import toastStyles from "react-toastify/ReactToastify.css?url";
    
    // Add the toast stylesheet
    export const links: LinksFunction = () => [{ rel: "stylesheet", href: toastStyles }];
    
    export const loader = async ({ request }: LoaderFunctionArgs) => {
      const { toast, headers } = await getToast(request);
      return data({ toast }, { headers });
    };
    
    export default function App() {
      const { toast } = useLoaderData<typeof loader>();
      // Hook to show the toasts
      useEffect(() => {
        if (toast) {
          // notify on a toast message
          notify(toast.message, { type: toast.type });
        }
      }, [toast]);
    
      return (
        <html lang="en">
          <head>...</head>
          <body
            >
            ...
            {/* Add the toast container */}
            <ToastContainer />
          </body>
        </html>
      );
    }
  9. Replace current history entry with a toast message

    main

    Use the replaceWith... utilities to replace the current entry in the browser's history stack and show a toast message. This is useful for redirects where you don't want the user to be able to click 'back' to the previous state.

    Available utilities:

    • replaceWithToast(path, options): General purpose replace with custom toast configuration.
    • replaceWithSuccess(path, messageOrOptions): Replaces history and shows a success toast.
    • replaceWithError(path, messageOrOptions): Replaces history and shows an error toast.
    • replaceWithInfo(path, messageOrOptions): Replaces history and shows an info toast.
    • replaceWithWarning(path, messageOrOptions): Replaces history and shows a warning toast.
    import { replaceWithToast } from "remix-toast";
    
    export const action = () => {
      return replaceWithToast("/login", {
        message: "You need to login to access this page",
        description: "description of toast",
        type: "error",
      });
    };