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
- A Remix V3 project with a router set up (see the fetch-router tutorial)
- A session cookie and storage backend (see the session overview)
What You Will Build
- A router with session middleware installed
- A page counter that increments on every visit
- A form that sets a user name in the session
- Flash messages after form submission
Step 1: Create Cookie and Storage
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
- Start your server and visit
http://localhost:3000 - You should see "Hello, stranger!" with a visit count of 1
- Refresh the page -- the count increments
- Enter a name and submit the form
- You should see a flash message ("Name set to...") and the greeting now uses your name
- Refresh again -- the flash message is gone, but the name and count persist
Summary
| Step | What Happens |
|---|---|
| Request arrives | Middleware parses the cookie and loads session data |
| Handler runs | context.get(Session) provides the session object |
Handler calls set/flash/etc. | Changes are tracked in memory |
| Handler returns response | Middleware saves the session and adds Set-Cookie header |
Next Steps
- Session Tutorial -- More session patterns including storage backend switching.
- API Reference -- Full documentation of the
session()function.