{"id":24866060,"url":"https://github.com/forge-42/remix-toast","last_synced_at":"2025-05-15T09:06:31.046Z","repository":{"id":198175399,"uuid":"700319647","full_name":"forge-42/remix-toast","owner":"forge-42","description":"Server side implementation of toast notifications in Remix","archived":false,"fork":false,"pushed_at":"2025-04-10T08:17:00.000Z","size":3286,"stargazers_count":243,"open_issues_count":4,"forks_count":11,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-08T14:07:26.088Z","etag":null,"topics":["remix-run","session","session-management","toast","toast-notifications"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/forge-42.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.MD","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["AlemTuzlak"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2023-10-04T11:23:58.000Z","updated_at":"2025-04-26T00:09:57.000Z","dependencies_parsed_at":null,"dependency_job_id":"0b8515dd-0d3c-4df0-9b32-7d9bde42275f","html_url":"https://github.com/forge-42/remix-toast","commit_stats":{"total_commits":30,"total_committers":5,"mean_commits":6.0,"dds":"0.23333333333333328","last_synced_commit":"f4a158db72fd1616e3ed854f1c8e138a8ac6a1d7"},"previous_names":["code-forge-net/remix-toast","forge42dev/remix-toast","forge-42/remix-toast"],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forge-42%2Fremix-toast","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forge-42%2Fremix-toast/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forge-42%2Fremix-toast/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forge-42%2Fremix-toast/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/forge-42","download_url":"https://codeload.github.com/forge-42/remix-toast/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254310515,"owners_count":22049469,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["remix-run","session","session-management","toast","toast-notifications"],"created_at":"2025-02-01T00:14:34.390Z","updated_at":"2025-05-15T09:06:31.023Z","avatar_url":"https://github.com/forge-42.png","language":"TypeScript","readme":"# Remix Toast\n![GitHub Repo stars](https://img.shields.io/github/stars/forge-42/remix-toast?style=social)\n![npm](https://img.shields.io/npm/v/remix-toast?style=plastic)\n![GitHub](https://img.shields.io/github/license/forge-42/remix-toast?style=plastic)\n![npm](https://img.shields.io/npm/dy/remix-toast?style=plastic) \n![npm](https://img.shields.io/npm/dw/remix-toast?style=plastic) \n![GitHub top language](https://img.shields.io/github/languages/top/forge-42/remix-toast?style=plastic)\n \n\n\u003cimg src=\"./assets/mascott.png\" alt=\"mascot\" width=\"200\" height=\"200\" align=\"right\" /\u003e\n\nSimple server-side toast management library for React router v7 / Remix.run!\n\nThis library provides you with all the essential utilities that you might need to\nshow toast notifications to your users. The client side implementation is completely\nup to you and you can use any library that you want to show the toasts.\n\nThe server function uses @remix-run/server-runtime primitives to create a cookie session so this\nlibrary is server agnostic and should work with any server setup.\n\nIf you wish to read an in depth explanation of how this works you can find it here:\nhttps://alemtuzlak.hashnode.dev/handling-toasts-in-remix\n## Installation\n\n```bash\nnpm install remix-toast\n```\n\n## Remix.run\n\nIf you are using Remix.run you can use v1.2.2 of this library or lower. V2 onwards is only react-router v7 compatible.\n\n### Migration guide to react-router v7\n\nIf you are using react-router v7 you can use v2.0.0 of this library. The only thing you have to change is rename all the `json` methods to `data` methods, the redirect methods stayed the same. For example:\n\n```diff\n- import { jsonWithSuccess } from \"remix-toast\";\n+ import { dataWithSuccess } from \"remix-toast\";\n\nexport const action = () =\u003e { \n- return jsonWithSuccess({ result: \"Data saved successfully\" }, \"Operation successful! 🎉\");\n+ return dataWithSuccess({ result: \"Data saved successfully\" }, \"Operation successful! 🎉\");\n};\n```\n\n## Setup\n\n### Server-side (middleware mode)\n\nIn order to be able to show toasts anywhere in the app you need to add the following code to your `root.tsx` file.\n\n```tsx\nimport { getToast, setToast, unstable_toastMiddleware } from \"remix-toast/middleware\";\n\nexport const loader = async ({ request, context }: Route.LoaderArgs) =\u003e {\n  // Extracts the toast from the request\n  const toast = getToast(context); \n  // pass it to the client side\n  return { toast }\n}\n\nexport const action = async ({ request, context }: Route.ActionArgs) =\u003e {\n  setToast(context, { message: \"hello toast!\", type: \"success\" }); \n  return null\n}\n\nexport default function App({ loaderData }: Route.ComponentArgs) {\n  const { toast } = loaderData\n  \n  useEffect(() =\u003e {\n   if(toast){\n    // Call your toast function here\n    alert(toast.message);\n   }\n  }, [toast])\n\n  return (\n    ...\n  );\n}\n\n// Export the middleware to be used in the app\nexport const unstable_middleware = [unstable_toastMiddleware()];\n\n```\n\n\n### Server-side\n\nIn order to be able to show toasts anywhere in the app you need to add the following code to your `root.tsx` file.\n\n```tsx\nimport { getToast } from \"remix-toast\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) =\u003e {\n  // Extracts the toast from the request\n  const { toast, headers } = await getToast(request);\n  // Important to pass in the headers so the toast is cleared properly\n  return data({ toast }, { headers });\n}\n\nexport default function App({ children }: { children: ReactNode }) {\n  const { toast } = useLoaderData\u003ctypeof loader\u003e();\n  \n  useEffect(() =\u003e {\n   if(toast){\n    // Call your toast function here\n    alert(toast.message);\n   }\n  }, [toast])\n\n  return (\n    ...\n  );\n}\n```\n### Client-side\n\nAfter this you can then use any toast notification library you prefer, but here are some examples:\n\n#### react-toastify\n\n```tsx\nimport { data, type LinksFunction, type LoaderFunctionArgs } from \"react-router\";\nimport { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration, useLoaderData } from \"react-router\";\nimport { useEffect } from \"react\";\nimport { getToast } from \"remix-toast\";\nimport { ToastContainer, toast as notify } from \"react-toastify\";\nimport toastStyles from \"react-toastify/ReactToastify.css?url\";\n\n// Add the toast stylesheet\nexport const links: LinksFunction = () =\u003e [{ rel: \"stylesheet\", href: toastStyles }];\n// Implemented from above\nexport const loader = async ({ request }: LoaderFunctionArgs) =\u003e {\n  const { toast, headers } = await getToast(request);\n  return data({ toast }, { headers });\n};\n\nexport default function App() {\n  const { toast } = useLoaderData\u003ctypeof loader\u003e();\n  // Hook to show the toasts\n  useEffect(() =\u003e {\n    if (toast) {\n      // notify on a toast message\n      notify(toast.message, { type: toast.type });\n    }\n  }, [toast]);\n\n  return (\n    \u003chtml lang=\"en\"\u003e\n      \u003chead\u003e\n        ...\n      \u003c/head\u003e\n      \u003cbody\u003e\n        ...\n        {/* Add the toast container */}\n        \u003cToastContainer /\u003e\n      \u003c/body\u003e\n    \u003c/html\u003e\n  );\n}\n```\n\n![react-toastify](./assets/react-toastify.gif) \n\n#### Sonner\n\n```tsx\nimport { data, type LinksFunction, type LoaderFunctionArgs } from \"react-router\";\nimport { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration, useLoaderData } from \"react-router\";\nimport { useEffect } from \"react\";\nimport { getToast } from \"remix-toast\";\nimport { Toaster, toast as notify } from \"sonner\";\n\n// Implemented from above\nexport const loader = async ({ request }: LoaderFunctionArgs) =\u003e {\n  const { toast, headers } = await getToast(request);\n  return data({ toast }, { headers });\n};\n\nexport default function App() {\n  const { toast } = useLoaderData\u003ctypeof loader\u003e();\n  // Hook to show the toasts\n  useEffect(() =\u003e {\n    if (toast?.type === \"error\") {\n      notify.error(toast.message);\n    }\n    if (toast?.type === \"success\") {\n      notify.success(toast.message);\n    }\n  }, [toast]);\n\n  return (\n    \u003chtml lang=\"en\"\u003e\n      \u003chead\u003e...\u003c/head\u003e\n      \u003cbody\u003e\n        ...\n        {/* Add the toast container */}\n        \u003cToaster /\u003e\n      \u003c/body\u003e\n    \u003c/html\u003e\n  );\n}\n```\n\n![react-toastify](./assets/sonner.gif) \n\n## Overriding cookie options\n\nYou can override the default cookie options by passing in your own options via the `setToastCookieOptions` function.\n\n```tsx\nimport { setToastCookieOptions } from \"remix-toast\";\n\nsetToastCookieOptions({ \n  secrets:\n    process.env.NODE_ENV === \"production\"\n      ? [process.env.SESSION_SECRET]\n      : [\"secret\"]\n});\n```\n\n## Creating utility functions with custom sessions\n\n`createToastUtilsWithCustomSession` is a function that allows you to create a custom session for your toasts. This is useful if you want to have different types of toasts for different parts of your app.\n\n```tsx\nimport { createCookieSessionStorage } from \"react-router\";\nimport { createToastUtilsWithCustomSession } from \"remix-toast\";\n\nconst session = createCookieSessionStorage({\n  cookie: {\n    name: \"your-custom-session\",\n    secrets: [\"some-secret\"],\n  },\n});\n\nexport const {\n  getToast,\n  redirectWithToast, \n  redirectWithSuccess, \n  redirectWithError, \n  redirectWithInfo, \n  redirectWithWarning, \n  dataWithSuccess, \n  dataWithError, \n  dataWithInfo, \n  dataWithWarning \n} = createToastUtilsWithCustomSession(session);\n```\n\n## Utilities\n\n## Redirect important notice!\n\nIf you want to use `throw` with any of the `redirectWith...` utilities you need to `await` the function call. This is because `redirectWith...` utilities have to generate a cookie and set it on the headers and if that is not awaited the headers won't be set properly and the redirect won't work.\n\n\n\n### redirectWithToast\n\nGeneral function that allows you to redirect to a new route and show a toast message.\n\n```tsx\nimport { redirectWithToast } from \"remix-toast\";\n\nexport const action = () =\u003e {\n  return redirectWithToast(\"/login\", { message: \"You need to login to access this page\", description: \"description of toast\", type: \"error\" });\n}\n\n```\n\n### redirectWithSuccess\n\nRedirects to a new route and shows a success toast message.\n\n```tsx\nimport { redirectWithSuccess } from \"remix-toast\";\n\nexport const action = () =\u003e {\n  return redirectWithSuccess(\"/login\", \"You are logged in!\"); \n  //or with description and message (works for all the other utilities as well)\n  return redirectWithSuccess(\"/login\", { message: \"You are logged in!\", description: \"description of toast\" });\n}\n\n```\n\n### redirectWithError\n\nRedirects to a new route and shows an error toast message.\n\n```tsx\nimport { redirectWithError } from \"remix-toast\";\n\nexport const action = () =\u003e {\n  return redirectWithError(\"/login\", \"You need to login to access this page\");\n}\n\n```\n\n### redirectWithInfo\n\nRedirects to a new route and shows an info toast message.\n\n```tsx\nimport { redirectWithInfo } from \"remix-toast\";\n\nexport const action = () =\u003e {\n  return redirectWithInfo(\"/login\", \"You need to login to access this page\");\n};\n```\n\n### redirectWithWarning\n\nRedirects to a new route and shows a warning toast message.\n\n```tsx\nimport { redirectWithWarning } from \"remix-toast\";\n\nexport const action = () =\u003e {\n  return redirectWithWarning(\"/login\", \"You need to login to access this page\");\n};\n```\n\n### dataWithSuccess\n\nDisplay a success toast message without a redirection.\n\n```tsx\nimport { dataWithSuccess } from \"remix-toast\";\n\nexport const action = () =\u003e {\n  return dataWithSuccess({ result: \"Data saved successfully\" }, \"Operation successful! 🎉\");\n   //or with description and message (works for all the other utilities as well)\n  return dataWithSuccess({ result: \"Data saved successfully\" }, { message: \"Operation successful! 🎉\", description: \"description of toast\" });\n};\n```\n\n### dataWithError\n\nDisplay an error toast message without a redirection.\n\n```tsx\nimport { dataWithError } from \"remix-toast\";\n\nexport const action = () =\u003e {\n  return dataWithError(null, \"Oops! Something went wrong. Please try again later.\");\n};\n```\n\n### dataWithInfo\n\nDisplay an info toast message without a redirection.\n\n```tsx\nimport { dataWithInfo } from \"remix-toast\";\n\nexport const action = () =\u003e {\n  return dataWithInfo({ info: \"Additional information\" }, \"Your profile has been successfully updated.\");\n};\n```\n\n### dataWithWarning\n\nDisplay a warning toast message without a redirection.\n\n```tsx\nimport { dataWithWarning } from \"remix-toast\";\n\nexport const action = () =\u003e {\n  return dataWithWarning({ warning: \"Potential issues\" }, \"Your session is about to expire.\");\n};\n```\n\n# Thank you\n\nIf you wish to support this project you can do so by starring this repository and sharing it with your friends.\n\nThanks to all the contributors on this project and the support to the community. You guys are awesome!\n","funding_links":["https://github.com/sponsors/AlemTuzlak"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fforge-42%2Fremix-toast","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fforge-42%2Fremix-toast","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fforge-42%2Fremix-toast/lists"}