Litro uses the pages/ directory for routing. Filenames map to URL paths:
| File | Route |
|---|---|
pages/index.ts | / |
pages/about.ts | /about |
pages/blog/index.ts | /blog |
pages/blog/[slug].ts | /blog/:slug |
pages/@[handle].ts | /@:handle |
pages/[...all].ts | /* (catch-all) |
A dynamic segment may carry a prefix before the bracket, as in @[handle] for
Mastodon-style /@alice URLs. A prefixed route wins over a bare [slug]
sibling. The prefix may use letters, digits and _ ~ @ ! & ' , ; = -.
A suffix after the bracket ([id].html.ts, [id]x.ts) is not supported and
the scanner rejects the file with an error. A catch-all or optional segment
cannot carry a prefix either.
A file in pages/ that is not a page, such as a story or a fixture, must not
use brackets in its name. Move it out of pages/, or start its name with -
so the scanner skips it.
Each page file exports a default Lit component class decorated with @customElement:
import { html } from 'lit';
import { customElement } from 'lit/decorators.js';
import { LitroPage } from '@beatzball/litro/runtime';
@customElement('page-about')
export class AboutPage extends LitroPage {
override render() {
return html`<h1>About</h1>`;
}
}
export default AboutPage;
Dynamic segments are available in definePageData via event.context.params:
export const pageData = definePageData(async (event) => {
const slug = event.context.params?.slug ?? '';
// ...
});
On the client side, onBeforeEnter(location) receives the parsed route parameters:
override onBeforeEnter(location: LitroLocation) {
this._slug = location.params.slug ?? '';
}
Export a routeMeta object to control the page's <title> and inject extra <head> HTML:
export const routeMeta = {
title: 'About — My Site',
head: '<meta name="description" content="About page" />',
};