Error: middleware must export a middleware or default function
ID: nextjs/middleware-export-error
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| 14 | active | — | — | — |
Root Cause
Next.js requires the middleware file (middleware.ts or middleware.js) at the project root (next to app/ or pages/) to export a named function called 'middleware' or a default export that is a function. This error occurs when the file exists but lacks the correct export, has a syntax error preventing the export from being recognized, is placed in the wrong directory, or exports a non-function value. Common causes include placing middleware.ts inside the app/ directory instead of the project root, using module.exports instead of ES module exports, or having TypeScript type errors that prevent compilation. The fix is to ensure the correct file location and proper named export.
genericWorkarounds
-
95% success Create middleware.ts at the project root with the correct named export
Create the file at {projectRoot}/middleware.ts (same level as package.json and the app/ directory): // middleware.ts import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export function middleware(request: NextRequest) { // Your middleware logic return NextResponse.next(); } // Optional: configure which routes middleware runs on export const config = { matcher: ['/dashboard/:path*', '/api/:path*'], }; The function must be named 'middleware' (named export) or be the default export. Both work, but the named export is the documented convention -
92% success Use the config.matcher export to control which routes trigger the middleware
Export a config object alongside the middleware function to control execution: export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico|public).*)'] }; This prevents the middleware from running on static assets and internal Next.js routes. Common matchers: specific paths: ['/dashboard/:path*'], API routes: ['/api/:path*'], all except static: ['/((?!_next/static|_next/image|favicon.ico).*)'], combining: ['/dashboard/:path*', '/api/:path*', '/auth/:path*']. The matcher uses a path-to-regexp syntax -
88% success Compose multiple middleware functions using a helper when you need modular middleware
Since Next.js only supports a single middleware file, compose multiple middleware concerns: // middleware.ts import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; function withAuth(request: NextRequest): NextResponse | null { const token = request.cookies.get('token'); if (!token && request.nextUrl.pathname.startsWith('/dashboard')) { return NextResponse.redirect(new URL('/login', request.url)); } return null; } function withLocale(request: NextRequest): NextResponse | null { const locale = request.headers.get('accept-language')?.split(',')[0]; if (locale && !request.nextUrl.pathname.startsWith(`/${locale}`)) { return NextResponse.rewrite(new URL(`/${locale}${request.nextUrl.pathname}`, request.url)); } return null; } export function middleware(request: NextRequest) { const authResponse = withAuth(request); if (authResponse) return authResponse; const localeResponse = withLocale(request); if (localeResponse) return localeResponse; return NextResponse.next(); } export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'] };
Dead Ends
Common approaches that don't work:
-
Placing middleware.ts inside the app/ directory or pages/ directory
95% fail
Next.js only recognizes the middleware file at the project root level (the same directory that contains app/ or pages/). Placing it inside app/middleware.ts or pages/middleware.ts causes it to be treated as a regular page or route, not as middleware. The middleware detection logic specifically looks for {projectRoot}/middleware.{ts,js}
-
Using module.exports or CommonJS syntax in the middleware file
90% fail
Next.js middleware runs in the Edge Runtime, which only supports ES modules. Using module.exports = function middleware() {} or exports.middleware = ... is not recognized. The middleware file must use ES module syntax with 'export function middleware()' or 'export default function()'. This is a common issue when copying patterns from older Node.js codebases or Express middleware
-
Exporting an object or configuration instead of a function from middleware.ts
85% fail
The middleware export must be a function that receives a NextRequest and returns a NextResponse or Response. Exporting a configuration object, a class, or a middleware factory without calling it will trigger this error. Even if using a higher-order function pattern like withAuth(handler), the final default export or named middleware export must be the resulting function, not the wrapper