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.