Display Flash Messages in Remix After Form Submission

Practical Remix Guide

In this article, we will learn how to display flash messages in the Remix framework after form submission. A flash message is a short message that appears to inform users about the outcome of an action they have taken, such as successfully registering or logging into an account.

Step 1: Create a Session Wrapper

The first step is to create a session wrapper that can be used to store and manage flash messages. Create a new file app/utils/sessions/flash.ts and add the following code:

import { createCookieSessionStorage } from '@remix-run/node';

// Get the secret for the cookie from environment variables
let secret = process.env.COOKIE_SECRET || 'default';

if (secret === 'default') {
  console.warn('🚨 No COOKIE_SECRET set, the app is insecure');
  secret = 'session-secret';
}

export const { getSession, commitSession, destroySession } = createCookieSessionStorage({
  cookie: {
    name: '__session',
    httpOnly: true,
    path: '/',
    sameSite: 'lax',
    secure: process.env.NODE_ENV === 'production',
    maxAge: 60,
    secrets: [secret],
  },
});

// Function to get flash message from the session
export async function getFlashMessage(request: Request) {
  const session = await getSession(request.headers.get('Cookie'));
  const message = session.get('message') || null;
  return { message, destroy: await destroySession(session) };
}

// Function to set flash message in the session
export async function setFlashMessage(request: Request, message: string) {
  const session = await getSession(request.headers.get('Cookie'));
  session.flash('message', message);
  return await commitSession(session);
}

In this section, we use the createCookieSessionStorage function to create a session storage that uses cookies. These cookies store the user's session data and have several important configurations such as security and cookie lifespan.

The getFlashMessage method is used to retrieve the message from the session and immediately destroy the session. The setFlashMessage method is used to set a message in the session that will be retrieved on the next request.

Step 2: Add the Register Route

Add a new register route to create a new user with a form. This route will handle the form submission and redirect the user to the login page with a flash message.

Create a new file app/routes/register/route.tsx with the following code:

import { redirect, type ActionFunctionArgs, type MetaFunction } from '@remix-run/node';
import { Form } from '@remix-run/react';
import { Text, TextLink } from '~/components/text';
import { appName, logoUrl } from '~/utils/helpers';
import { setFlashMessage } from '~/utils/sessions/flash';
import { registerAction } from './action'; // Function to handle creating a new user

export const meta: MetaFunction = () => {
  return [{ title: `Sign Up - ${appName}` }];
};

export async function action({ request }: ActionFunctionArgs) {
  const { errors, message } = await registerAction(request);

  if (!errors) {
    return redirect('/login', {
      headers: {
        'Set-Cookie': await setFlashMessage(request, message),
      },
    });
  }

  return { errors };
}

export default function Register() {
  return (
    <div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
      <div className="sm:mx-auto sm:w-full sm:max-w-sm">
        <img className="mx-auto h-10 w-auto" src={logoUrl} alt={appName} />
        <h2 className="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900 dark:text-white">
          Create an account
        </h2>
      </div>

      <div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
        <Form method="post" className="bg-white px-6 py-12 shadow sm:rounded-lg sm:px-12 dark:bg-zinc-900">
          <input type="text" name="name" placeholder="Name" className="input-class" required />
          <input type="email" name="email" placeholder="Email" className="input-class" required />
          <input type="password" name="password" placeholder="Password" className="input-class" required />
          <button type="submit" className="btn-class">Create User</button>
        </Form>

        <Text className="mt-10 text-center">
          Already have an account? <TextLink href="/login">Sign in here</TextLink>.
        </Text>
      </div>
    </div>
  );
}

The registerAction method handles creating a new user and sets errors and success messages. If there are no errors, the user will be redirected to the login page with the flash message set in the session.

Step 3: Add the Login Route

Add a new login route to display the flash message. The loader in this route will retrieve the flash message from the session and display it on the login page.

Create a new file app/routes/login/route.tsx with the following code:

import { LoaderFunctionArgs, json, type MetaFunction } from '@remix-run/node';
import { Form, useLoaderData } from '@remix-run/react';
import { Badge } from '~/components/badge';
import { Text, TextLink } from '~/components/text';
import { appName, logoUrl } from '~/utils/helpers';
import { getFlashMessage } from '~/utils/sessions/flash';

export const meta: MetaFunction = () => {
  return [{ title: `Sign In - ${appName}` }];
};

export async function loader({ request }: LoaderFunctionArgs) {
  const session = await getFlashMessage(request);

  return json(
    { message: session.message },
    {
      headers: {
        'Set-Cookie': session.destroy,
      },
    }
  );
}

export default function Login() {
  const { message } = useLoaderData<typeof loader>();

  return (
    <div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
      <div className="text-center sm:mx-auto sm:w-full sm:max-w-sm">
        <img className="mx-auto h-10 w-auto" src={logoUrl} alt={appName} />
        <h2 className="mt-10 text-2xl font-bold leading-9 tracking-tight text-gray-900 dark:text-white">
          Sign in to your account
        </h2>
        {message && (
          <Badge color="cyan" className="text-center">
            {message}
          </Badge>
        )}
      </div>

      <div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
        <Form method="POST" className="bg-white px-6 py-12 shadow sm:rounded-lg sm:px-12 dark:bg-zinc-900">
          <input type="email" name="email" placeholder="Email" className="input-class" required />
          <input type="password" name="password" placeholder="Password" className="input-class" required />
          <button type="submit" className="btn-class">Sign In</button>
        </Form>

        <Text className="mt-10 text-center">
          Not a member? <TextLink href="/register">Sign up here</TextLink>.
        </Text>
      </div>
    </div>
  );
}

This loader uses the getFlashMessage function to get the message from the session and automatically destroy the session after the message is retrieved. The message is then displayed using the Badge component.

Conclusion

By following the steps above, you can now add a flash message feature to your Remix application. This provides better feedback to users after they perform certain actions such as registration or login.

References:

  1. Show a Flash Message in Remix After Form Submission