Skip to content

Routing FAQ

Frequently asked questions about routing in EreoJS.

How do optional segments work?

Use double brackets [[param]] for segments that may or may not be present in the URL:

routes/posts/[[page]].tsx

This matches both /posts and /posts/2. Inside the loader, check whether the parameter exists:

tsx
export const loader = createLoader(async ({ params }) => {
  const page = params.page ? parseInt(params.page) : 1
  return { posts: await getPosts({ page }) }
})

Optional segments only apply to the last segment of a route path. You cannot have optional segments in the middle of a path.

How do I add trailing slashes?

By default, EreoJS does not enforce trailing slashes. To add them, configure the router in ereo.config.ts:

ts
// ereo.config.ts
import { defineConfig } from '@ereo/core'

export default defineConfig({
  routes: {
    trailingSlash: 'always', // 'always' | 'never' | 'ignore'
  },
})
  • 'always' --- Redirects /about to /about/
  • 'never' --- Redirects /about/ to /about (default)
  • 'ignore' --- Accepts both forms without redirecting

What is the route priority order?

When multiple routes could match a URL, EreoJS resolves them in this order:

  1. Static routes --- Exact path matches (/about, /posts/new)
  2. Dynamic routes --- Single parameter segments (/posts/[id])
  3. Optional routes --- Optional segments (/posts/[[page]])
  4. Catch-all routes --- Rest parameters (/docs/[...slug])

Within the same priority level, routes defined in alphabetically earlier directories take precedence. For example, routes/posts/new.tsx (static) always wins over routes/posts/[id].tsx (dynamic) for the URL /posts/new.

Can I have multiple layouts?

Yes. Each directory can have its own _layout.tsx, and layouts nest automatically:

routes/
  _layout.tsx              # Root layout (html, head, body)
  (marketing)/
    _layout.tsx            # Marketing layout (header, footer)
    index.tsx              # /
    pricing.tsx            # /pricing
  (app)/
    _layout.tsx            # App layout (sidebar, nav)
    dashboard.tsx          # /dashboard
    settings.tsx           # /settings

Route groups (marketing) and (app) let you apply different layouts to different sections without affecting URLs. A route inherits all layouts from its parent directories.

What is the middleware execution order?

Middleware runs from outermost to innermost, following the directory structure:

routes/
  _middleware.ts           # 1. Runs first (root)
  dashboard/
    _middleware.ts         # 2. Runs second
    settings/
      _middleware.ts       # 3. Runs third
      index.tsx            # Handler

Each middleware calls next() to pass control to the next middleware or the route handler. After the handler responds, control flows back through the middleware in reverse order:

ts
export const middleware: MiddlewareHandler = async (request, context, next) => {
  // Before handler
  console.log('Before')
  const response = await next()
  // After handler
  console.log('After')
  return response
}

Named middleware configured in ereo.config.ts runs before file-based middleware in the order they are listed in the route's config.middleware array.

How do API routes differ from page routes?

API routes export HTTP method handlers (GET, POST, PUT, DELETE) and return Response objects. They do not render React components:

ts
// routes/api/users.ts
export async function GET({ request }) {
  const users = await db.users.findMany()
  return Response.json({ users })
}

Page routes export a default React component and optionally a loader/action. They render HTML:

tsx
// routes/users/index.tsx
export const loader = createLoader(async () => {
  return { users: await db.users.findMany() }
})

export default function Users({ loaderData }) {
  return <ul>{loaderData.users.map(u => <li key={u.id}>{u.name}</li>)}</ul>
}

API routes are typically placed under routes/api/ by convention, but any route file that exports HTTP method handlers is treated as an API route regardless of its location.

Can I colocate non-route files in the routes directory?

Files that do not export a default component or HTTP method handlers are ignored by the router. However, to keep things clean, it is recommended to place utilities, types, and shared components outside the routes/ directory (e.g., in src/lib/ or src/components/).

How do I create a 404 page?

Add a _404.tsx file at the root of your routes directory:

tsx
// routes/_404.tsx
export default function NotFound() {
  return (
    <div>
      <h1>404 - Page Not Found</h1>
      <a href="/">Go Home</a>
    </div>
  )
}

See the Routing concepts page for the full routing reference.

Released under the MIT License.