openElement uses filesystem routing. A route module exports a page definition, and the framework wires params, data, metadata, and app shell rendering.
| File | URL |
|---|---|
app/routes/index.tsx | / |
app/routes/about.tsx | /about |
app/routes/docs/index.tsx | /docs |
app/routes/posts/[slug].tsx | /posts/:slug |
import { definePage } from '@openelement/app';
export default definePage({
route: { path: '/about' },
head: { title: 'About' },
render() {
return <main>About</main>;
},
});Bracket segments become route params. The flat params are also passed as props for simple cases.
import { definePage } from '@openelement/app';
export default definePage({
route: { path: '/posts/[slug]', params: ['slug'] },
render({ params }) {
return <article>Post: {params.slug}</article>;
},
});Use load() when data should be fetched by the framework before rendering. This is the openElement equivalent of the route data functions that mainstream file-route frameworks expose.
import { definePage } from '@openelement/app';
type Post = { title: string; body: string };
export default definePage<Post>({
route: { path: '/posts/[slug]', params: ['slug'] },
head: { title: 'Post' },
async load({ params }) {
return fetch(`https://api.example.com/posts/${params.slug}`)
.then((r) => r.json());
},
render({ data }) {
return (
<article>
<h1>{data.title}</h1>
<p>{data.body}</p>
</article>
);
},
});The same descriptor can set document title, description, route intent, and revalidation intent. Build adapters read it as structured metadata.
export default definePage({
route: { path: '/field-notes' },
head: {
title: 'Field Notes',
description: 'Posts about Web Components',
meta: [{ property: 'og:type', content: 'article' }],
},
renderIntent: { mode: 'static', revalidate: 300 },
render() {
return <main>...</main>;
},
});| File | Purpose |
|---|---|
_renderer.ts | Wraps route VNodes for layout composition. |
_middleware.ts | Mounts Hono middleware for headers, auth, CSP, and request guards. |
api/*.ts | Defines Hono API handlers inside the route tree. |
@openelement/content plugin from the Vite config.