Defining Routes

A Router maps URL paths, such as /home and /about, to the components rendered for those paths.

The router tracks the current URL and selects the matching components. Retend includes this router, so no additional routing library is required.

Setting Up Routes

Define an array of Route Records to configure application routing. Each record maps a URL path to a component.

Use the defineRoutes helper to create the array:

import { Router, defineRoutes, createRouterRoot } from 'retend/router';
import { renderToDOM } from 'retend-web';

// 1. Create your page components
const Home = () => <div>Welcome to the Home Page</div>;
const About = () => <div>About Us</div>;

// 2. Define your routes
const routes = defineRoutes([
  { path: '/', component: Home },
  { path: '/about', component: About },
]);

// 3. Initialize the Router
const router = new Router({ routes });
const root = document.getElementById('app')!;

// 4. Render the router to the screen
renderToDOM(root, () => createRouterRoot(router));

The createRouterRoot function converts the router into a renderable component.

Nested Routes and Layouts

Applications often share layout elements, such as a header or navigation sidebar, across pages.

Nested Routes let a parent route provide the layout for its child routes. Use <Outlet /> to mark where the child component is rendered.

import { defineRoutes, Outlet } from 'retend/router';

function DashboardLayout() {
  return (
    <div class="dashboard-layout">
      <aside>Sidebar Navigation</aside>
      <main>
        {/* The child page content will appear here */}
        <Outlet />
      </main>
    </div>
  );
}

const Overview = () => <h2>Dashboard Overview</h2>;
const Settings = () => <h2>User Settings</h2>;

const routes = defineRoutes([
  {
    path: '/dashboard',
    component: DashboardLayout,
    children: [
      { path: 'overview', component: Overview },
      { path: 'settings', component: Settings },
    ],
  },
]);

At /dashboard/overview, the router renders DashboardLayout and places Overview at <Outlet />. At /dashboard/settings, the layout remains rendered and the outlet content changes.

Default Child Routes

At /dashboard, <Outlet /> is empty if no child route matches. Define a default child route with an empty path (''):

const routes = defineRoutes([
  {
    path: '/products',
    component: ProductsLayout,
    children: [
      { path: '', component: ProductList }, // This loads by default at /products
      { path: 'categories', component: CategoriesList },
    ],
  },
]);

Dynamic URLs

Use dynamic routes when a URL contains variable data, such as a user ID or post title. Prefix a path segment with a colon (:):

const routes = defineRoutes([
  // This matches /users/123, /users/alice, etc.
  { path: '/users/:userId', component: UserProfile },

  // You can even have multiple dynamic segments
  { path: '/posts/:category/:postId', component: BlogPost },
]);

The following sections describe how to read values such as userId from components.

Handling "404 Not Found"

Define a custom "Not Found" page for URLs that do not match another route by using an asterisk (*) as the path.

The router checks routes from top to bottom, so place the catch-all route last:

const routes = defineRoutes([
  { path: '/', component: Home },
  { path: '/about', component: About },

  // If nothing above matched, this will run
  { path: '*', component: NotFoundPage },
]);

The router can now select a component for each matching URL.