Skip to content

Tutorial: Add Session Middleware to a Router

In this tutorial, you will add session middleware to a Remix router, read and write session data in route handlers, and use flash messages for post-redirect feedback.

Prerequisites

What You Will Build

  1. A router with session middleware installed
  2. A page counter that increments on every visit
  3. A form that sets a user name in the session
  4. Flash messages after form submission
ts
// app/session.ts
import { createCookie } from 'remix/cookie'
import { createCookieSessionStorage } from 'remix/session/cookie-storage'

export let sessionCookie = createCookie('__session', {
  httpOnly: true,
  secure: process.env.NODE_ENV === 'production',
  sameSite: 'Lax',
  secrets: [process.env.SESSION_SECRET!],
  maxAge: 60 * 60 * 24 * 7, // 1 week
})

export let sessionStorage = createCookieSessionStorage(sessionCookie)

Step 2: Add Session Middleware to the Router

ts
// app/server.ts
import { createRouter } from 'remix/fetch-router'
import { session } from 'remix/session-middleware'
import { Session } from 'remix/session'
import { route } from 'remix/fetch-router/routes'
import { sessionCookie, sessionStorage } from './session.ts'

let routes = route({
  home: '/',
  setName: '/set-name',
})

let router = createRouter({
  middleware: [
    session(sessionCookie, sessionStorage),
  ],
})

The session() function takes two arguments: the cookie configuration and the storage backend. It returns middleware that you add to the router's middleware array.

Step 3: Read and Write Session Data

Build a page that counts visits and greets the user by name:

ts
router.get(routes.home, async ({ context }) => {
  let s = context.get(Session)

  // Increment visit count
  let visits = (s.get('visits') ?? 0) + 1
  s.set('visits', visits)

  // Read user name and flash message
  let name = s.get('name') ?? 'stranger'
  let notice = s.get('notice') // flash -- consumed on read

  return new Response(
    `<!DOCTYPE html>
    <html>
    <body>
      ${notice ? `<p style="color: green">${notice}</p>` : ''}
      <h1>Hello, ${name}!</h1>
      <p>You have visited this page ${visits} time(s).</p>
      <form method="POST" action="/set-name">
        <input type="text" name="name" placeholder="Your name" required />
        <button type="submit">Set Name</button>
      </form>
    </body>
    </html>`,
    { headers: { 'Content-Type': 'text/html' } },
  )
})

You do not need to call a save method -- the session middleware handles that automatically after the handler returns.

Step 4: Handle Form Submission with Flash Messages

ts
router.post(routes.setName, async ({ context }) => {
  let formData = await context.request.formData()
  let name = formData.get('name') as string

  let s = context.get(Session)
  s.set('name', name)
  s.flash('notice', `Name set to "${name}"!`)

  // Post/Redirect/Get pattern
  return new Response(null, {
    status: 302,
    headers: { Location: '/' },
  })
})

The flash message is set with session.flash(). When the browser follows the redirect to /, the handler calls session.get('notice'), which returns the message and removes it from the session. A second visit to / will not show the message.

Step 5: Test It

  1. Start your server and visit http://localhost:3000
  2. You should see "Hello, stranger!" with a visit count of 1
  3. Refresh the page -- the count increments
  4. Enter a name and submit the form
  5. You should see a flash message ("Name set to...") and the greeting now uses your name
  6. Refresh again -- the flash message is gone, but the name and count persist

Summary

StepWhat Happens
Request arrivesMiddleware parses the cookie and loads session data
Handler runscontext.get(Session) provides the session object
Handler calls set/flash/etc.Changes are tracked in memory
Handler returns responseMiddleware saves the session and adds Set-Cookie header

Next Steps

Released under the MIT License.