A full-stack TypeScript framework you can read top to bottom.

One explicit page registry drives browser navigation, server rendering, and static generation — so you can trace a URL to its component without guessing what a file-based convention decided for you.

Start a projectnpx @askrjs/cli@latest create startkit my-app

This is the whole routing layer.

No folder-name magic, no generated manifest to reverse-engineer. Routes, layouts, and the data they load are declared once, in code you can open and read.

import {
  createRouteRegistry,
  fallback,
  group,
  route,
} from '@askrjs/askr/router';

export const pageRegistry = createRouteRegistry(() => {
  group({ layout: AppLayout }, () => {
    route('/', OverviewPage);
    route('/activity', ActivityPage);
    fallback(NotFoundPage);
  });
});
  1. 01

    The route table is a value, not a global

    Every path, layout, and param lives in this one registry — which you export and hand to whatever renders it. Nothing is filled in behind you by a folder-naming convention.

  2. 02

    Pick your rendering mode later

    Write the app once. Ship it as an SPA, hydrate it from server HTML, or pre-render it — swap modes without a rewrite.

  3. 03

    Server code lives next to the routes it serves

    Auth policies, schemas, and dependencies are wired in at the same root, so you can see what a route requires.

  4. 04

    Deploy a folder of HTML or a small Node process

    Nothing exotic to operate — static hosting or a plain adapter, your call.

Start with the application you need to ship.

Choose the closest starter, keep the generated files in view, and add rendering, server, and production capabilities only when the application calls for them.

Create an app