Ecopages0.2.0-rc.4

Routing

Ecopages uses file-based routing for pages and programmatic routing for API endpoints.

File-Based Routing

Every file in your src/pages directory corresponds to a route. The path relative to src/pages determines the URL.

Supported File Extensions

Extensions depend on your active integrations:

Index Routes

  • src/pages/index.tsx/
  • src/pages/blog/index.tsx/blog

Nested Routes

  • src/pages/about/team.tsx/about/team

Dynamic Routes

Use bracket syntax: [param].

  • src/pages/blog/[slug].tsx/blog/:slug
  • src/pages/users/[id]/profile.tsx/users/:id/profile

Access params via staticPaths and staticProps on eco.page views, or at request time with cache: 'dynamic'.

Catch-all Routes

  • src/pages/docs/[...slug].tsx/docs/a, /docs/a/b, etc.

Programmatic API Routing

API routes are defined on your app instance in app.ts:

import { createApp } from '@ecopages/core/create-app';
import appConfig from './eco.config';
 
const app = await createApp({ appConfig });
 
app.get('/api/users/:id', async ({ params, response }) => {
	return response.json({ userId: params.id });
});
 
await app.start();

Use app.add(handler) for handlers created with defineApiHandler. See Define Handlers.

Route Priority

  1. API / explicit routes in app.ts
  2. Static files in public
  3. File-based pages in src/pages

Route Configuration

Set the canonical base URL in eco.config.ts:

import { ConfigBuilder } from '@ecopages/core/config-builder';
import { ecopagesJsxPlugin } from '@ecopages/ecopages-jsx';
 
const config = await new ConfigBuilder()
	.setBaseUrl('https://example.com')
	.setIntegrations([ecopagesJsxPlugin()])
	.build();

404 Page

Create 404.tsx (or your integration extension) under src/pages. Unmatched page requests render this template with status 404.

500 Page

Create 500.tsx (or your integration extension) under src/pages. When a page render fails in the file-route pipeline, Ecopages renders this template with status 500.

Important:

  • Applies to page rendering (matched routes and a failing custom 404). API handlers keep their own errorHandler / JSON response path.
  • If the 500.* template is missing or throws, the response falls back to plain-text Internal Server Error. The custom page is never retried.
  • In development, the thrown error is passed as page props (message, stack) so the custom 500 page can show the real failure. In production, those props are omitted so stacks are not serialized into HTML. The original error is always logged server-side.
  • Like 404.*, the file is also a normal route at /500, so you can open it directly to preview the design (that direct visit is a 200 and has no error props unless you trigger a render failure).
  • Semantic error templates render outside the normal page-request middleware pipeline. They receive a safe empty pageLocals object instead of request-scoped locals. Do not use cache: 'dynamic', middleware, or requires on 404.* / 500.* templates.
import { eco, type Error500TemplateProps } from '@ecopages/core';
 
export default eco.page<Error500TemplateProps>({
	render: ({ message, stack }) => (
		<div>
			<h1>Something went wrong</h1>
			{message ? <p>{message}</p> : null}
			{stack ? <pre>{stack}</pre> : null}
		</div>
	),
});

Summary

TypeDefinitionUsage
Static pagessrc/pages/*.{ext}Marketing, docs, blogs
Dynamic pagessrc/pages/[slug].{ext}Posts, profiles
Error pagessrc/pages/404.*, src/pages/500.*Not-found and server-error HTML
API endpointsapp.get('/api/...')Data, forms