/posts/42
routes/posts/[id].tsxposts/[id].jsxid = "42"/posts/new
routes/posts/new.tsxposts/new.jsxstatic wins/about
routes/about.tsxabout.jsxprerenderedPages and handlers
A default export is the React page. Without an explicit GET handler, Goribu renders it directly:
export const meta = { title: "About" };
// This page has no request-time data to load.
export default function AboutPage() {
return <main><h1>About our app</h1></main>;
}
export const meta = { title: "About" };
// This page has no request-time data to load.
export default function AboutPage() {
return <main><h1>About our app</h1></main>;
}
- Request data: Export a named HTTP handler when the response needs request data.
- Page response: Return
res.render(props)to render the page in the same route file. - API endpoints: A route with only handlers does not need a React component.
File-based URLs
| File | URL |
|---|---|
src/routes/index.tsxsrc/routes/index.jsx | / |
src/routes/about.tsxsrc/routes/about.jsx | /about |
src/routes/posts/index.tsxsrc/routes/posts/index.jsx | /posts |
src/routes/posts/new.tsxsrc/routes/posts/new.jsx | /posts/new |
src/routes/posts/[id].tsxsrc/routes/posts/[id].jsx | /posts/42 |
- Extensions: Route files may use
.js,.jsx,.ts, or.tsx. - Index routes: An
indexfile maps to its containing folder. - Excluded files: TypeScript declaration files are ignored so they can be colocated.
- Symlinks: Route discovery does not follow symlinks.
Dynamic parameters
Square brackets capture one URL segment. You can read the value as a string from req.params:
import type { Handler } from "goribu/server";
type Props = { name: string };
export const GET: Handler<Props> = (req, res) => {
return res.render({ name: req.params.name });
};
export default function HelloPage({ name }: Props) {
return <h1>Hello, {name}.</h1>;
}
export const GET = (req, res) => {
return res.render({ name: req.params.name });
};
export default function HelloPage({ name }) {
return <h1>Hello, {name}.</h1>;
}
- Parameter names: Dynamic parameter names may contain letters, digits and underscores. Each name must be distinct within one route.
- Unsupported segments: Catch-all and optional segments are not supported.
Pre-rendered pages
To run a GET handler at build time, export prerender = true on a fixed-path page without middleware. This page captures the build timestamp:
import type { Handler } from "goribu/server";
type Props = { builtAt: string };
export const prerender = true;
export const GET: Handler<Props> = (req, res) => {
return res.render({ builtAt: new Date().toISOString() });
};
export default function BuildInfoPage({ builtAt }: Props) {
return <p>Built at {builtAt}</p>;
}
export const prerender = true;
export const GET = (req, res) => {
return res.render({ builtAt: new Date().toISOString() });
};
export default function BuildInfoPage({ builtAt }) {
return <p>Built at {builtAt}</p>;
}
- Automatic candidates: A page will be automatically pre-rendered when its URL is known at build time, has no HTTP handlers or selected middleware and has not opted out explicitly.
- Production delivery: After a successful pre-render, Cloudflare Static Assets serves the HTML and saved navigation data. Document loads, client navigation and prefetch avoid Worker invocations and their cost.
Pre-rendering fixes the server output at build time. The page can still hydrate, use React state and run client-side navigation.
Use a route export to make the choice explicit:
export const prerender = false;
export const prerender = false;
- Opt out:
prerender = falsekeeps an otherwise static page on the Worker. - Require static output:
prerender = truerequires static output and fails the build if Goribu cannot produce it. - GET at build time: With
prerender = true, GET runs during the production build instead of on each request. Visitors receive the saved page, which stays unchanged until the next build. - Incompatible routes:
prerender = truecannot be combined with non-GET handlers, dynamic segments or middleware. - Fixed data: Pre-rendered HTML and navigation data share the same build-time props and metadata. Changing query strings does not rerun an explicitly pre-rendered GET handler or server metadata functions.
- Development: Pages use the Worker during development for freshness.
- Build fallback: A static candidate that cannot render at build time normally falls back to the Worker with a build message.
- Suspense failures: Since 0.9.3, a candidate whose
Suspenseboundary throws during the build stays on the Worker with a build warning, rather than shipping only its fallback. Withprerender = true, the build fails. - Separate actions: A static page can submit a Form to a separate action route.
Routing rules
- Required exports: Every route needs a page or at least one HTTP handler, but it can have both.
- Conflicting routes: Two files with the same URL shape fail the build.
posts/[id].tsxposts/[id].jsxandposts/[slug].tsxposts/[slug].jsxconflict. - Underscore names: Names beginning with
_are not URLs. Underscore-prefixed folders exclude their subtree. - Server helpers: Keep
.server.*helpers outsidesrc/routes/and import them from handlers or middleware. - Browser code restrictions: Route files use explicit named re-exports;
export *, bare.server.*side-effect imports and directevalare rejected in browser-reachable route code. See Server-only work. - Reserved URLs:
/_goribu/is reserved for framework assets and protocols. - Public HTML: Do not place public HTML files at URLs already owned by routes.
- Prebuilt 404: Since 0.9.3,
404.htmlis handled by Goribu's prebuilt 404 page, so customize_404instead. - Special route files:
_404,_500, and folder-level_middlewarehave special roles under routes. See Errors and Request handlers. - App-wide Document: The optional Document file is located separately in
src/setup/_document.tsxsrc/setup/_document.jsx. See Rendering.
HTTP defaults
- GET: A page has an implicit GET. An explicit handler answers its own method.
- HEAD: Goribu derives HEAD from GET when needed.
- OPTIONS: Automatic OPTIONS returns
204with anAllowheader. - Unsupported methods: Goribu returns
405for unsupported methods. - Unmatched URLs: Unmatched URLs return 404. Since 0.9.3, production serves URLs that no Worker route can match from Static Assets without invoking the Worker. See Which requests run the Worker.
- Trailing slashes: Trailing slashes redirect to the slash-free URL, except for
/. Static assets use the same canonical shape.