Defining Routes

In most web applications, you want to show different pages when the user visits different URLs (like /home or /about). In Retend, we do this using a Router.

The router keeps track of the current URL and decides which components to show on the screen. Retend includes its own router built-in, so you don't need to install any extra libraries!

Setting Up Routes

To set up your application's routing, you define an array of Route Records. Each record simply connects a URL path to a component.

We use the defineRoutes helper function to create this 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 takes your router and turns it into a component that can be rendered.

Nested Routes and Layouts

Most applications share common layout elements across different pages—like a header at the top or a navigation sidebar on the left.

Instead of copying and pasting the header into every single page component, you can use Nested Routes. You define a parent route that acts as the layout, and use the special <Outlet /> component to mark where the child pages should be 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 },
    ],
  },
]);

Now, when a user visits /dashboard/overview, the DashboardLayout appears on the screen, and the Overview component is placed right where the <Outlet /> is. When they navigate to /dashboard/settings, the sidebar stays exactly where it is, and only the <Outlet /> part changes.

Default Child Routes

If a user visits exactly /dashboard, the <Outlet /> would be empty because no child route was specified. You can create a default child route by using an empty string ('') as the path:

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

Dynamic URLs

Sometimes URLs need to contain variable data, like a specific user's ID or a blog post's title. You can create a dynamic route by adding a colon (:) before a segment in the path:

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 },
]);

We will cover how to actually access those variables (like the userId) from inside your components in the next few sections.

Handling "404 Not Found"

If a user visits a URL that doesn't exist in your routes, you probably want to show a custom "Not Found" page. You can use an asterisk (*) to create a route that matches absolutely anything.

Because the router checks routes in order from top to bottom, always put your "catch-all" route at the very end:

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

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

With your routes defined, your application now knows exactly what to show for any URL.