Skip to main content
Resolid provides flexible routing systems that integrate with React Router, supporting both convention-based file routing and programmatic route configuration.

Route Generation

Resolid offers two approaches to defining routes:
  1. Flex Routes - Convention-based file system routing
  2. Relative Factory - Programmatic route definition with relative paths

Flex Routes

Flex routes automatically generate route configurations from your file system structure.

Setup

resolid.config.ts
import { defineDevConfig } from "@resolid/dev";
import { flexRoutes } from "@resolid/dev/routes";

export const { reactRouterConfig } = defineDevConfig({
  reactRouterConfig: {
    async routes() {
      return await flexRoutes({
        routesDirectory: "routes",
        ignoredRouteFiles: ["**/*.test.ts", "**/*.css"],
      });
    },
  },
});

Options

interface FolderRoutesOptions {
  routesDirectory?: string;      // Default: "routes"
  ignoredRouteFiles?: string[];  // Glob patterns to ignore
}

File Conventions

Flex routes support these file extensions:
  • .js, .jsx
  • .ts, .tsx
  • .md, .mdx

Route Files

File names map to URL paths:
routes/
  index.tsx           → /
  about.tsx           → /about
  blog/
    index.tsx         → /blog
    $slug.tsx         → /blog/:slug
  users/
    $id.tsx           → /users/:id
    $id/
      edit.tsx        → /users/:id/edit

Dynamic Segments

Prefix with $ for dynamic route parameters:
$postId.tsx           → /:postId
$userId/posts.tsx     → /:userId/posts
The $ at the end becomes a splat route:
$.tsx                 → /*
docs/$.tsx            → /docs/*

Index Routes

Files named _index create index routes:
routes/
  _index.tsx          → /
  blog/
    _index.tsx        → /blog

Layout Routes

Files ending with _layout create layout routes:
routes/
  admin_layout.tsx    → Layout for /admin/*
  admin/
    users.tsx         → /admin/users (uses admin_layout)
    settings.tsx      → /admin/settings (uses admin_layout)

Pathless Routes

Prefix with _ to create pathless routes:
routes/
  _auth/
    login.tsx         → /login (no /auth in URL)
    register.tsx      → /register

Escaped Characters

Use [] to escape special characters:
[about].tsx           → /about (literal, not special)
api/[users].tsx       → /api/users (literal brackets)

Optional Segments

Use () for optional route segments:
(lang)/about.tsx      → /about and /:lang/about
docs/($version).tsx   → /docs and /docs/:version

Route Manifest

Flex routes convert files into a route manifest:
type RouteManifest = {
  [routeId: string]: {
    path?: string;
    index?: boolean;
    caseSensitive?: boolean;
    id: string;
    parentId?: string;
    file: string;
  };
};
The manifest is then converted to React Router’s route config format.

Conflict Resolution

When multiple files resolve to the same route:
routes/
  about.tsx
  about/
    _index.tsx
Resolid logs a warning and uses the first file encountered. Consolidate into a single file to resolve conflicts.

Relative Factory

The relative factory enables programmatic route definitions with path resolution relative to a directory.

Setup

import { relativeFactory } from "@resolid/dev/routes";
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const { route, index, layout } = relativeFactory(__dirname);

export default [
  index("pages/home.tsx"),
  route("/about", "pages/about.tsx"),
  layout("layouts/dashboard.tsx", [
    route("/users", "pages/users.tsx"),
    route("/settings", "pages/settings.tsx"),
  ]),
];

API

The factory returns three functions that mirror React Router’s route helpers:

route(path, file, children?)

Define a route with a path:
route("/blog/:slug", "pages/blog-post.tsx")
With children:
route("/blog", "pages/blog.tsx", [
  index("pages/blog-index.tsx"),
  route(":slug", "pages/blog-post.tsx"),
])

index(file, children?)

Define an index route:
index("pages/home.tsx")

layout(file, children?)

Define a layout route:
layout("layouts/app.tsx", [
  index("pages/dashboard.tsx"),
  route("/profile", "pages/profile.tsx"),
])

Path Resolution

All file paths are resolved relative to the factory’s directory:
// In /app/config/routes.ts
const { route } = relativeFactory("/app/config");

route("/about", "../pages/about.tsx");
// Resolves to: /app/pages/about.tsx
// Route file path: pages/about.tsx (relative to app directory)

Advanced Patterns

Nested Routing

Combine layouts and child routes:
routes/
  app_layout.tsx
  app/
    dashboard_layout.tsx
    dashboard/
      _index.tsx      → /app/dashboard
      stats.tsx       → /app/dashboard/stats
    settings.tsx      → /app/settings

Route Groups

Organize routes without affecting URLs:
routes/
  (marketing)/
    _layout.tsx       → Layout for marketing pages
    index.tsx         → /
    about.tsx         → /about
  (app)/
    _layout.tsx       → Layout for app pages
    dashboard.tsx     → /dashboard

Mixed Approach

Combine flex routes with manual routes:
resolid.config.ts
import { flexRoutes, relativeFactory } from "@resolid/dev/routes";
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const { route } = relativeFactory(__dirname);

export const { reactRouterConfig } = defineDevConfig({
  reactRouterConfig: {
    async routes() {
      const autoRoutes = await flexRoutes();
      
      return [
        ...autoRoutes,
        // Add custom routes
        route("/legacy", "legacy/index.tsx"),
      ];
    },
  },
});

Route Metadata

Access route information in your components:
import { useMatches } from "react-router";

export default function MyRoute() {
  const matches = useMatches();
  
  // matches contains route hierarchy
  matches.forEach((match) => {
    console.log(match.id);       // Route ID
    console.log(match.pathname); // Current path
    console.log(match.params);   // Route params
  });
}

Route Configuration Example

A complete route configuration:
resolid.config.ts
import { defineDevConfig } from "@resolid/dev";
import { flexRoutes } from "@resolid/dev/routes";

export const { reactRouterConfig } = defineDevConfig({
  appDirectory: "src",
  
  reactRouterConfig: {
    async routes() {
      return await flexRoutes({
        routesDirectory: "routes",
        ignoredRouteFiles: [
          "**/*.test.{ts,tsx}",
          "**/*.spec.{ts,tsx}",
          "**/*.css",
          "**/*.scss",
          "**/components/**",
        ],
      });
    },
  },
});

TypeScript Support

React Router generates types for your routes automatically. Import them using the +types convention:
routes/blog/$slug.tsx
import type { Route } from "./+types/route";

export async function loader({ params }: Route.LoaderArgs) {
  const slug = params.slug; // TypeScript knows this exists
  // ...
}

export default function BlogPost({ loaderData }: Route.ComponentProps) {
  // loaderData is fully typed
}
The types are generated based on your route file location and structure.

Build docs developers (and LLMs) love