rr-next-routes
Version:
generate nextjs-style routes in your react-router-v7 application
256 lines (204 loc) • 7.94 kB
Markdown
<div align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/vniehues/rr-next-routes/main/assets/rr-next-routes-dark.svg">
<img alt="rr-next-routes logo" src="https://raw.githubusercontent.com/vniehues/rr-next-routes/main/assets/rr-next-routes-light.svg" height="128">
</picture>
<h1>rr-next-routes</h1>
<a href="https://www.npmjs.com/package/rr-next-routes"><img alt="NPM version" src="https://img.shields.io/npm/v/rr-next-routes?style=for-the-badge"></a>
<a href="https://github.com/vniehues/rr-next-routes/blob/main/license.md"><img alt="License" src="https://img.shields.io/npm/l/rr-next-routes?style=for-the-badge"></a>
</div>
## Features
`rr-next-routes` is a utility library for generating **Next.js-style** routes for your **React Router v7** or **Remix** applications.
It provides a directory-based routing solution for React applications, similar to Next.js, and supports features like layouts, route parameters, and dynamic routing with nested route management.
<details>
<summary><b>Motivation</b></summary>
<br>
I really enjoy using file-based (directory-based) routing when working with next.js
<br>
While there are different solutions like [generouted](https://github.com/oedotme/generouted), most of them require you to modify multiple files and some even bring their own routing.
**rr-next-routes** is a simple drop in solution for project using [remix](https://remix.run) or [react-router v7](https://reactrouter.com/home) in framework mode.
you can even still use the [manual routing](https://reactrouter.com/start/framework/routing) to add more routes to your liking while **rr-next-routes** takes care of the pages dir:
#### **`routes.ts`**
``` typescript
import {route, type RouteConfig} from "@react-router/dev/routes";
import {nextRoutes, appRouterStyle} from "rr-next-routes/react-router";
//import {nextRoutes, appRouterStyle} from "rr-next-routes/remix";
const autoRoutes = nextRoutes({
...appRouterStyle,
print: "tree",
});
export default [
...autoRoutes,
route("some/path", "./some/file.tsx"),
] satisfies RouteConfig;
```
</details>
## Features
#### Keep using **React Router v7** in framework mode and still get:
- Generate route configurations from a file-based structure.
- Supports for (nested) layouts
- Handles dynamic routes, optional parameters, and catch-all routes (`[param].tsx`, `[[param]].tsx`, `[...param].tsx`).
- Compatible with **React Router v7** (framework mode) and **Remix** routing systems.
- Predefined configurations to suit both app-router and page-router patterns.
- Flexible configuration system.
- Configurable printing options: `info`, `table`, or `tree` to console log the generation results
## Installation
You can install the library using npm:
``` bash
npm install rr-next-routes
```
## Usage
### Example
Here’s a sample usage of `rr-next-routes` for generating route configurations:
#### **`routes.ts`**
``` typescript
import { type RouteConfig } from "@react-router/dev/routes";
import {nextRoutes, appRouterStyle} from "rr-next-routes/react-router";
// for remix use this instead
// import {nextRoutes, appRouterStyle} from "rr-next-routes/remix";
const routes = nextRoutes({ print: "info" });
export default routes satisfies RouteConfig;
```
## Configuration Options
The `nextRoutes` function accepts an optional configuration object of type `Options`:
#### `Options` Type
```typescript
type PrintOption = "no" | "info" | "table" | "tree";
type Options = {
folderName?: string; // Folder to scan for routes (default: "pages").
print?: PrintOption; // Controls printing output (default: "info").
layoutFileName?: string; // Name of layout files (default: "_layout").
routeFileNames?: string[]; // Names for route files (e.g., ["page", "index"]).
routeFileNameOnly?: boolean; // Restrict routes to matching routeFileNames.
extensions?: string[]; // File extensions to process (e.g., [".tsx", ".ts"]).
};
```
### Predefined Styles
#### 1. `appRouterStyle (default)`
Best for projects following the Next.js app-router convention:
```typescript
export const appRouterStyle: Options = {
folderName: "",
print: "info",
layoutFileName: "layout",
routeFileNames: ["page", "route"],
extensions: [".tsx", ".ts", ".jsx", ".js"],
routeFileNameOnly: true,
};
```
#### 2. `pageRouterStyle`
Best for projects following the Next.js pages-router convention:
```typescript
export const pageRouterStyle: Options = {
folderName: "pages",
print: "info",
layoutFileName: "_layout",
routeFileNames: ["index"],
extensions: [".tsx", ".ts", ".jsx", ".js"],
routeFileNameOnly: false,
};
```
### Example Configurations
#### Default Configuration:
```typescript
const routes = nextRoutes(); // Uses default options (appRouterStyle).
```
#### Using Pages Router:
```typescript
const routes = nextRoutes(pageRouterStyle);
```
#### Custom App Router:
```typescript
const routes = nextRoutes({
...appRouterStyle,
print: "tree",
layoutFileName: "_customLayout",
});
```
### Example File Structure (pageRouterStyle)
```
app/
├── pages/
│ ├── index.tsx
│ ├── about.tsx
│ ├── dashboard/
│ │ ├── _layout.tsx
│ │ ├── index.tsx
│ │ ├── settings.tsx
│ ├── profile/
│ │ ├── name.tsx
│ │ ├── email.tsx
│ │ ├── password.tsx
```
### Generated Routes
The above structure will result in the following routes:
- `/` → `pages/index.tsx`
- `/about` → `pages/about.tsx`
- `/dashboard` → `pages/dashboard/index.tsx` (wrapped by `layout`: `pages/dashboard/_layout.tsx`)
- `/dashboard/settings` → `pages/dashboard/settings.tsx` (wrapped by `layout`: `pages/dashboard/_layout.tsx`)
- `/profile/name` → `pages/profile/name.tsx`
- `/profile/email` → `pages/profile/email.tsx`
- `/profile/password` → `pages/profile/password.tsx`
## Dynamic Routes
The library supports Next.js-style dynamic and optional routes:
### Example
#### File Structure
```
app/
├── pages/
│ ├── [id].tsx
│ ├── [[optional]].tsx
│ ├── [...all].tsx
```
#### Generated Routes
- `/:id` → `pages/[id].tsx`
- `/:optional?` → `pages/[[optional]].tsx`
- `/*` → `pages/[...all].tsx`
These routes are created using a special character mapping:
| File Name | Route Path |
| --- | --- |
| `[id].tsx` | `/:id` |
| `[[optional]].tsx` | `/:optional?` |
| `[...all].tsx` | `/*` |
## Deploying on Vercel
When deploying to Vercel, you may run into a vague build error:
> An unexpected error happened when running this build. We have been notified of the problem.
This can happen when your `routes.ts` file doesn’t explicitly include an `index()` route. Even if `nextRoutes()` correctly generates a route for `/`, Vercel's build process may still fail unless that route is defined using `index()`.
### Fix
Manually define the index route and filter out the `/` path from the generated routes:
```ts
import { nextRoutes, pageRouterStyle } from "rr-next-routes/react-router";
import { route, index } from "@react-router/dev/routes";
const generatedRoutes = nextRoutes({ ...pageRouterStyle, print: "tree" });
const otherRoutes = generatedRoutes.filter(r => r.path !== "/");
const routes = [
index("./pages/index.tsx"),
...otherRoutes
];
export default routes;
```
No need to modify `rr-next-routes` itself – just include the `index()` call as shown above to ensure Vercel deploys your app without issues.
## Testing
This project uses **Vitest** for testing. To run the tests:
``` bash
npm test
```
## Development
The project supports ES modules and is built using `tsup`.
### Build
To build the project:
``` bash
npm run build
```
### Watch (Test in Development Mode)
``` bash
npm run dev
```