Server-Side Toast Notifications for Remix

4.0.0 · active · verified Sun Apr 19

remix-toast provides utility functions to manage server-side toast notifications within Remix applications, leveraging web standards like `Set-Cookie` for flash messages. It seamlessly integrates with Remix's `loader` and `action` functions and supports React Router v7. The current stable version is 4.0.0, which includes an upgrade to Zod v4. The package appears to have an active release cadence, with frequent updates (minor and patch releases) to address bug fixes, introduce new features like `replaceWithFlash`, and maintain compatibility with new versions of Remix and React Router (e.g., v7.9+ for `react-router`). Key differentiators include its server-side first approach, middleware support for global toast management, and flexible session storage configuration, allowing custom utility creation with `createToastUtilsWithCustomSession`.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to set up server-side toast notifications in a Remix application using `remix-toast`. It covers configuring a session storage, initializing the `toast` utility, extracting and displaying toasts in a `loader` and `action`, and rendering them on the client-side.

import { createCookieSessionStorage, redirect } from '@remix-run/node';
import { createToast, getToast, unstable_toastMiddleware } from 'remix-toast/middleware';
import { json } from '@remix-run/react';
import type { ActionFunctionArgs, LoaderFunctionArgs } from '@remix-run/node';
import { useLoaderData } from '@remix-run/react';

// 1. Configure a session storage (replace with your actual session store)
const { getSession, commitSession } = createCookieSessionStorage({
  cookie: {
    name: '__session',
    httpOnly: true,
    maxAge: 60 * 60 * 24 * 7, // 1 week
    secrets: [process.env.SESSION_SECRET ?? 'super-secret'],
    secure: process.env.NODE_ENV === 'production',
  },
});

// 2. Setup toast middleware
export const { toast, flash } = createToast(getSession, commitSession);

export const loader = async ({ request }: LoaderFunctionArgs) => {
  const { toast, headers } = await getToast(request);
  // Simulate fetching some data
  const data = { message: 'Welcome back!' };
  return json({ data, toast }, { headers });
};

export const action = async ({ request }: ActionFunctionArgs) => {
  const formData = await request.formData();
  const email = formData.get('email');

  if (email === 'error@example.com') {
    return await toast.error(redirect('/'), { message: 'Invalid email address.' });
  }

  return await toast.success(redirect('/'), { message: 'Subscription successful!' });
};

export default function Index() {
  const { toast } = useLoaderData<typeof loader>();

  return (
    <div>
      {toast && (
        <div style={{ padding: '10px', background: toast.type === 'error' ? 'red' : 'green', color: 'white' }}>
          {toast.message}
        </div>
      )}
      <h1>Remix Toast Example</h1>
      <form method="post">
        <input type="email" name="email" placeholder="Enter email" />
        <button type="submit">Subscribe</button>
      </form>
    </div>
  );
}

view raw JSON →