Middleware is code that runs before your route's loader or action. Think of it as a checkpoint — every request passes through it before reaching your page.
Without middleware, every route that needs a logged-in user has to call requireUser itself:
export async function loader({ request }) {
let user = await requireUser(request)
// ... use user
}
That works, but you repeat the same line in every protected route. Middleware moves the check to the layout so it runs once for all child routes.
/app layout exports a middleware array with [requireUserMiddleware]./app/* route loads, the middleware runs first.requireUser, gets the user, and stores it in the route's context.requireUser again.Here's what the layout looks like:
// app/routes/app/_layout.tsx
import { requireUserMiddleware } from '~/.server/auth/middlewares'
export const middleware = [requireUserMiddleware]
That single line protects every route under /app. Child routes can read the user from context when they need it:
import { requireUser } from '~/.server/auth/middlewares'
export async function loader({ context }) {
let user = requireUser(context)
// ... use user
}
The real benefit is peace of mind. If you add ten routes under /app, they're all protected automatically. No chance of forgetting the check in one loader. The middleware handles it at the layout level, and every child route inherits the protection.