There is no loader, because a page is already the loader

Building·4 min· Assumes: async

Here is the API you are looking for, and it does not exist. No getServerSideProps, no exported load, no data function sitting above the component waiting to hand it an object.

A page receives the request in its props, and an async computed that reads them is the load. You already know both halves. The only new part is where the request comes from.

A page takes props, and the router fills them in

import type { PageProps } from '@markless/router';

export function App({ url, params }: PageProps) @{
	// url.href, url.pathname, url.search
	// params.slug, for a page called [slug].tsrx
}

params holds the pieces of the path that were in brackets, so a page at pages/blog/[slug].tsrx gets params.slug. url is the address that was asked for, split into the parts you normally want.

Then the load is the thing you would have written anyway, and the waiting and the failure are the blocks you already know. Here is the whole page file:

import { computed } from '@markless/core';
import type { PageProps } from '@markless/router';
import { fetchLocalUpdates } from '../lib/updates.ts';

export default function App({ url }: PageProps) @{
	const feed = computed(async () => fetchLocalUpdates(url.href));

	@try {
		<p class="channel">{feed.channel}</p>
	} @pending {
		<p class="pending">Checking local updates...</p>
	} @catch {
		<p class="error">Local updates unavailable</p>
	}
}

That is a computed whose body is async. It depends on url because it read url, so a different URL is a different value, worked out again, with nothing wired up by you. The derive function takes no arguments: on 0.3.1 the published type is computed<T>(derive: () => T), so there is nothing handed in for you to cancel with.

The server does not wait for the slow part

This is worth understanding, because it changes what you put on a page. The server does not hold the whole document until every boundary settles:

The server flushes the document with each still-pending boundary showing its @pending arm between its existing comment anchors, and keeps the response open. When a boundary settles, the server appends to the same stream.

The settled block arrives as an inert <template>, which the browser parses without rendering, and a small executor moves that content into place. In the specification's words: "The pending arm is genuinely replaced, not hidden and kept."

Try it: step through one request

Press "Step forward" three times, and read the sentence each time. This is an illustration of a sequence you cannot watch inside a demo box, not a live request:

Request

Nothing yet. The request has just arrived at the server.

The last step is cheap because a streamed commit is one fragment move plus record registration, and nothing else. The framework has no runtime copy of the document to reconcile the new content against.

When the reader clicks through to another page whose data is slow, the outgoing page stays live and interactive until the destination settles. You do not have to build that behaviour, and you do not have to hold a spinner over the whole layout to get it.

What about API routes and middleware?

They are top-level folders beside pages/, not inside it, and each is an ordinary module with a default export. An api/health.ts exports a function returning a Response, so the whole file can be one line of work. A middleware/request.ts exports a function taking the request context, and it runs before the request reaches a page, which is where a header, a redirect or an auth check belongs. Note that pages/api/ is not supported: a file there is a page with a confusing name.

Try it yourself

Give a page an async computed that reads params, and put a slow call in it. Load the page and watch the @pending block appear and then be replaced. Then add a second boundary beside the first with a faster call, and notice that the fast one does not wait for the slow one, because they are separate boundaries and each settles on its own.

Next: what the compiler is doing underneath all of this.