Authlance Routing & Metadata

Part of the Authlance Extension Guide.

The authlance/loop-scaffold workspace can be used as template that already wires every major Authlance dashboard surface: route discovery, document providers, prerender caching, contextual table actions, and layout slots. This chapter zooms in on the routing and metadata pieces by following the fictional Taco Fish Stand extension (@eltacofish).


Routing & Navigation

Registering routes

Routes are discovered through RoutesApplicationContribution. Each contribution returns a RouteContribution that the shell consumes both for the router and for building navigation menus.

@injectable()
class TacoMenuPageContribution implements RoutesApplicationContribution {
  getRoute(): RouteContribution {
    return {
      path: '/tacos/menu',
      name: 'Menu',
      component: TacoMenuPage,
      navBar: true,
      icon: <TruckIcon />,
      category: new RouteCategory('Taco Fish', <FishIcon />, 15),
      root: true,
      exact: true,
      authRequired: true,
      forceParent: '/',
    }
  }
}

// category example
const TACOF_CATEGORY = new RouteCategory('Taco Fish', <FishIcon />);

@injectable()
class TacoSearchContribution implements RoutesApplicationContribution {
  getRoute(): RouteContribution {
    return {
      path: '/tacos/search',
      name: 'Search Tacos',
      component: TacoSearchPage,
      navBar: true,
      category: TACOF_CATEGORY,
      exact: true,
      root: true,
      forceParent: '/',
      authRequired: true,
    }
  }
}

A route is considered “root” when it is not nested under another route that renders a React Router outlet. By default the router infers parentage from the path (for example, /tacos/menu automatically nests under /tacos). Use forceParent when you need to pin the route beneath a different parent—e.g., forcing both /tacos/menu and /tacos/search to live under /, meaning the / route is the one rendering their outlet.

Adding a RouteCategory to a contribution groups that route under the same label inside both the main sidebar and the Docs sidebar. If a route does not have a parent category to be a navBar item, it needs to provide an icon and to be navBar: true to show up in the sidebar.

Bind every contribution in your frontend container module so the router sees it:

bind(TacoMenuPageContribution).toSelf()
bind(RoutesApplicationContribution).toService(TacoMenuPageContribution)

The RoutesProvider (already wired by @authlance/core) collects all bound contributions. The authenticated sidebar (@authlance/sidebar’s AppSidebar) filters the resulting list to routes where navBar: true, applies the optional RouteCategory, and hides entries that the current user cannot access (roles + group-role checks). React Router will not register routes that the user cannot access either.

Grouping navigation

RouteCategory controls grouping and ordering. Categories share a label/icon and accept an optional weight so, for example, Taco Fish links can sit ahead of Billing links. Inside a category the sidebar sorts by route name. Stand-alone routes without a category appear as top-level items. Because AppSidebar relies solely on these route attributes, you never have to touch layout code to add a new nav entry—registering the contribution is enough.


Route Metadata & Document Providers

One of the Authlance features is the ability to have your components rendered on the server and to provide SEO metadata. Authlance exposes two SEO hooks:

  1. Route-level metadata via the prerender.document field.
  2. Dashboard-level metadata via DefaultDashboardContentContributor#getDocumentProvider.

Route-level SEO

Any route can return static or dynamic metadata as part of its prerender config. The Taco Fish Stand search page injects canonical links and Open Graph tags like this:

const TACOF_CATEGORY = new RouteCategory('Taco Fish', <FishIcon />);

@injectable()
class TacoSearchContribution implements RoutesApplicationContribution {
  getRoute(): RouteContribution {
    return {
      path: '/tacos/search',
      name: 'Search Orders',
      component: TacoSearchPage,
      navBar: true,
      category: TACOF_CATEGORY,
      exact: true,
      root: false,
      authRequired: true,
      prerender: {
        enabled: true,
        document: async (ctx) => ({
          title: buildSearchTitle(ctx.query),
          links: [{ attributes: { rel: 'canonical', href: buildCanonicalUrl(ctx) } }],
          meta: [
            { attributes: { name: 'description', content: summarizeResults(ctx.extraParams.results) } },
            { attributes: { property: 'og:title', content: 'Taco Fish search' } },
          ],
        }),
      },
    }
  }
}

document can return either a RoutePrerenderDocumentDefinition or a RoutePrerenderDocumentProvider promise. The backend HTML renderer (DefaultHtmlRenderer) normalizes the result and injects it into the server-rendered shell before React hydrates.

Dashboard landing metadata

The dashboard landing view is provided by the DefaultDashboardContentContributor. Implement getDocumentProvider to describe metadata for / (or whichever route hosts your default content). The Taco Fish Stand component returns the hero copy and keywords that appear on the marketing home page without touching individual routes.