create-monolayer-app 0.1.0-canary.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -0
- package/bin/index.js +179 -0
- package/package.json +29 -0
- package/template/.agents/skills/react-router/SKILL.md +122 -0
- package/template/.agents/skills/react-router/references/data-mode.md +165 -0
- package/template/.agents/skills/react-router/references/declarative-mode.md +123 -0
- package/template/.agents/skills/react-router/references/framework-mode.md +213 -0
- package/template/.agents/skills/react-router/references/rsc.md +90 -0
- package/template/.claude/CLAUDE.md +126 -0
- package/template/.dockerignore +4 -0
- package/template/.vscode/settings.json +62 -0
- package/template/AGENTS.md +126 -0
- package/template/README.md +87 -0
- package/template/_gitignore +7 -0
- package/template/app/app.css +16 -0
- package/template/app/root.tsx +76 -0
- package/template/app/routes/home.tsx +13 -0
- package/template/app/routes.ts +3 -0
- package/template/app/welcome/logo-dark.svg +23 -0
- package/template/app/welcome/logo-light.svg +23 -0
- package/template/app/welcome/welcome.tsx +89 -0
- package/template/oxfmt.config.ts +7 -0
- package/template/oxlint.config.ts +34 -0
- package/template/package.json +34 -0
- package/template/public/favicon.ico +0 -0
- package/template/react-router.config.ts +7 -0
- package/template/tsconfig.json +27 -0
- package/template/vite.config.ts +10 -0
package/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# create-monolayer-app
|
|
2
|
+
|
|
3
|
+
The official CLI starter kit for creating Monolayer applications.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
You can create a new Monolayer app using `npm create` or `npx`:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Using npm create (recommended)
|
|
11
|
+
npm create monolayer-app <app-name>
|
|
12
|
+
|
|
13
|
+
# Using npx
|
|
14
|
+
npx create-monolayer-app <app-name>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Replace `<app-name>` with your desired project name (must be a slug without spaces, e.g. `my-monolayer-app`).
|
|
18
|
+
|
|
19
|
+
## What's Included
|
|
20
|
+
|
|
21
|
+
The scaffolded project includes:
|
|
22
|
+
|
|
23
|
+
- **React Router v8** — Server-side rendering, routing, and data loading
|
|
24
|
+
- **React 19** — Latest React features
|
|
25
|
+
- **Tailwind CSS v4** — Modern utility-first styling with Vite integration
|
|
26
|
+
- **TypeScript** — Full type safety out of the box
|
|
27
|
+
- **Ultracite (Oxlint + Oxfmt)** — Fast linting and formatting
|
|
28
|
+
- **AI Agent Context** — Preconfigured `.agents`, `.claude`, and `AGENTS.md` guidelines
|
|
29
|
+
|
|
30
|
+
## Next Steps
|
|
31
|
+
|
|
32
|
+
Once your project is scaffolded and dependencies are installed:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
cd <app-name>
|
|
36
|
+
npm run dev
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Available Scripts
|
|
40
|
+
|
|
41
|
+
- `npm run dev` — Start the development server with HMR at `http://localhost:5173`
|
|
42
|
+
- `npm run build` — Create an optimized production build
|
|
43
|
+
- `npm run start` — Run the production server
|
|
44
|
+
- `npm run check` — Run typegen, TypeScript compiler, and Ultracite checks
|
|
45
|
+
- `npm run fix` — Automatically format and fix lint issues
|
|
46
|
+
|
|
47
|
+
## Requirements
|
|
48
|
+
|
|
49
|
+
- Node.js >= 18.0.0
|
|
50
|
+
- npm >= 9.0.0
|
|
51
|
+
|
|
52
|
+
## License
|
|
53
|
+
|
|
54
|
+
MIT
|
package/bin/index.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { spawnSync } from 'node:child_process';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
|
|
11
|
+
// Terminal color helpers
|
|
12
|
+
const reset = '\x1b[0m';
|
|
13
|
+
const bold = (str) => `\x1b[1m${str}${reset}`;
|
|
14
|
+
const cyan = (str) => `\x1b[36m${str}${reset}`;
|
|
15
|
+
const green = (str) => `\x1b[32m${str}${reset}`;
|
|
16
|
+
const red = (str) => `\x1b[31m${str}${reset}`;
|
|
17
|
+
const dim = (str) => `\x1b[2m${str}${reset}`;
|
|
18
|
+
const yellow = (str) => `\x1b[33m${str}${reset}`;
|
|
19
|
+
|
|
20
|
+
// Helper to validate slug
|
|
21
|
+
function isValidSlug(name) {
|
|
22
|
+
if (!name || typeof name !== 'string') return false;
|
|
23
|
+
if (/\s/.test(name)) return false;
|
|
24
|
+
// Slug should consist of letters, numbers, hyphens, and underscores
|
|
25
|
+
return /^[a-zA-Z0-9_-]+$/.test(name);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Copy directory recursively
|
|
29
|
+
function copyDir(src, dest) {
|
|
30
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
31
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
32
|
+
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
if (entry.name === '.DS_Store') continue;
|
|
35
|
+
|
|
36
|
+
const srcPath = path.join(src, entry.name);
|
|
37
|
+
let destName = entry.name;
|
|
38
|
+
|
|
39
|
+
// npm ignores or modifies .gitignore during publishing, so we use _gitignore in template
|
|
40
|
+
if (destName === '_gitignore') {
|
|
41
|
+
destName = '.gitignore';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const destPath = path.join(dest, destName);
|
|
45
|
+
|
|
46
|
+
if (entry.isDirectory()) {
|
|
47
|
+
copyDir(srcPath, destPath);
|
|
48
|
+
} else {
|
|
49
|
+
fs.copyFileSync(srcPath, destPath);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function printUsage() {
|
|
55
|
+
console.log(`
|
|
56
|
+
${bold('create-monolayer-app')} - Scaffold a new Monolayer application
|
|
57
|
+
|
|
58
|
+
${bold('Usage:')}
|
|
59
|
+
npx create-monolayer-app ${green('<app-name>')}
|
|
60
|
+
|
|
61
|
+
${bold('Arguments:')}
|
|
62
|
+
${green('<app-name>')} Required. The name of the app (slug, no spaces). A directory
|
|
63
|
+
with this name will be created in the current working directory.
|
|
64
|
+
|
|
65
|
+
${bold('Options:')}
|
|
66
|
+
-h, --help Show this help message
|
|
67
|
+
-v, --version Show version number
|
|
68
|
+
|
|
69
|
+
${bold('Example:')}
|
|
70
|
+
npx create-monolayer-app my-monolayer-app
|
|
71
|
+
`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function main() {
|
|
75
|
+
const args = process.argv.slice(2);
|
|
76
|
+
|
|
77
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
78
|
+
printUsage();
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
83
|
+
const pkgPath = path.join(__dirname, '../package.json');
|
|
84
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
85
|
+
console.log(pkg.version);
|
|
86
|
+
process.exit(0);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const rawArg = args.find((arg) => !arg.startsWith('-'));
|
|
90
|
+
|
|
91
|
+
if (!rawArg) {
|
|
92
|
+
console.error(red('Error: <app-name> is required.'));
|
|
93
|
+
printUsage();
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const appName = rawArg.trim();
|
|
98
|
+
|
|
99
|
+
if (appName.length === 0) {
|
|
100
|
+
console.error(red('Error: App name cannot be empty.'));
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (/\s/.test(appName)) {
|
|
105
|
+
console.error(red(`Error: App name "${appName}" contains spaces.`));
|
|
106
|
+
console.error(yellow('App name must be a slug without spaces (e.g. my-app or my_app).'));
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!isValidSlug(appName)) {
|
|
111
|
+
console.error(red(`Error: App name "${appName}" is not a valid slug.`));
|
|
112
|
+
console.error(yellow('Only alphanumeric characters, hyphens (-), and underscores (_) are allowed.'));
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const targetDir = path.resolve(process.cwd(), appName);
|
|
117
|
+
const templateDir = path.resolve(__dirname, '../template');
|
|
118
|
+
|
|
119
|
+
if (!fs.existsSync(templateDir)) {
|
|
120
|
+
console.error(red(`Error: Template directory not found at ${templateDir}`));
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (fs.existsSync(targetDir)) {
|
|
125
|
+
const existingFiles = fs.readdirSync(targetDir);
|
|
126
|
+
if (existingFiles.length > 0) {
|
|
127
|
+
console.error(red(`Error: Directory "${appName}" already exists and is not empty.`));
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
console.log(`\nCreating a new Monolayer app in ${green(targetDir)}...\n`);
|
|
133
|
+
|
|
134
|
+
// 1. Copy template files
|
|
135
|
+
copyDir(templateDir, targetDir);
|
|
136
|
+
|
|
137
|
+
// 2. Update package.json name
|
|
138
|
+
const targetPkgPath = path.join(targetDir, 'package.json');
|
|
139
|
+
if (fs.existsSync(targetPkgPath)) {
|
|
140
|
+
const pkg = JSON.parse(fs.readFileSync(targetPkgPath, 'utf8'));
|
|
141
|
+
pkg.name = appName.toLowerCase();
|
|
142
|
+
fs.writeFileSync(targetPkgPath, `${JSON.stringify(pkg, null, 2)}\n`, 'utf8');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// 3. Install dependencies with npm
|
|
146
|
+
console.log(cyan('Installing dependencies with npm...'));
|
|
147
|
+
console.log(dim(`Running npm install in ${targetDir}\n`));
|
|
148
|
+
|
|
149
|
+
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
150
|
+
const installResult = spawnSync(npmCmd, ['install'], {
|
|
151
|
+
cwd: targetDir,
|
|
152
|
+
stdio: 'inherit',
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
if (installResult.status !== 0) {
|
|
156
|
+
console.error(red('\nFailed to install dependencies with npm.'));
|
|
157
|
+
process.exit(installResult.status ?? 1);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// 4. Success message
|
|
161
|
+
console.log(`\n${green('✔')} Successfully created ${bold(appName)} at ${dim(targetDir)}\n`);
|
|
162
|
+
console.log('Inside that directory, you can run:');
|
|
163
|
+
console.log(`\n ${cyan(`cd ${appName}`)}`);
|
|
164
|
+
console.log(` ${cyan('npm run dev')}\n`);
|
|
165
|
+
console.log('Available scripts:');
|
|
166
|
+
console.log(` ${cyan('npm run dev')} - Start the development server with HMR`);
|
|
167
|
+
console.log(` ${cyan('npm run build')} - Build the application for production`);
|
|
168
|
+
console.log(` ${cyan('npm run start')} - Start the production server`);
|
|
169
|
+
console.log(` ${cyan('npm run check')} - Run typegen, TypeScript, and Ultracite checks`);
|
|
170
|
+
console.log(` ${cyan('npm run fix')} - Automatically fix lint and format issues\n`);
|
|
171
|
+
console.log(bold('Happy coding!'));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
main();
|
|
176
|
+
} catch (err) {
|
|
177
|
+
console.error(red('\nAn unexpected error occurred:'), err);
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-monolayer-app",
|
|
3
|
+
"version": "0.1.0-canary.1",
|
|
4
|
+
"description": "CLI to scaffold a new Monolayer application",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-monolayer-app": "./bin/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"publish:local": "npm publish --registry http://localhost:4873",
|
|
11
|
+
"unpublish:local": "npm unpublish --registry http://localhost:4873 create-monolayer-app -f"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"bin",
|
|
15
|
+
"template"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=22.0.0"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"create-monolayer-app",
|
|
22
|
+
"create-app",
|
|
23
|
+
"monolayer",
|
|
24
|
+
"react-router",
|
|
25
|
+
"scaffold",
|
|
26
|
+
"template"
|
|
27
|
+
],
|
|
28
|
+
"license": "MIT"
|
|
29
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react-router
|
|
3
|
+
description: Build applications with React Router in Framework, Data, Declarative, and unstable RSC modes. Use when configuring routes, route modules, loaders, actions, forms, fetchers, navigation, pending UI, SSR/SPA/pre-rendering, middleware, URL params/search params, or React Router upgrades.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# React Router
|
|
8
|
+
|
|
9
|
+
React Router is mode-specific. Before changing an app, identify the mode, load the matching reference, then read the installed docs for the installed package version.
|
|
10
|
+
|
|
11
|
+
## Identify the Mode
|
|
12
|
+
|
|
13
|
+
Do not apply Framework/Data patterns to a Declarative app unless you are intentionally migrating modes.
|
|
14
|
+
|
|
15
|
+
### Framework Mode
|
|
16
|
+
|
|
17
|
+
Use Framework Mode guidance when you see:
|
|
18
|
+
|
|
19
|
+
- `@react-router/dev` in dependencies
|
|
20
|
+
- `react-router.config.ts`
|
|
21
|
+
- `app/routes.ts`
|
|
22
|
+
- `app/entry.server.tsx` and/or `app/entry.client.tsx` files
|
|
23
|
+
- route modules under `app/routes/`
|
|
24
|
+
- route exports like `loader`, `action`, `clientLoader`, `clientAction`, `ErrorBoundary`, `meta`, `links`, or `headers`
|
|
25
|
+
- imports from `./+types/...`
|
|
26
|
+
- the React Router Vite plugin from `@react-router/dev/vite`
|
|
27
|
+
|
|
28
|
+
Framework examples usually use the default `app/` directory, but check `react-router.config.ts` for a custom `appDirectory` before assuming exact paths.
|
|
29
|
+
|
|
30
|
+
Then read `references/framework-mode.md`.
|
|
31
|
+
|
|
32
|
+
### Data Mode
|
|
33
|
+
|
|
34
|
+
Use Data Mode guidance when you see:
|
|
35
|
+
|
|
36
|
+
- `createBrowserRouter`, `createHashRouter`, `createMemoryRouter`, or `createStaticRouter`
|
|
37
|
+
- `<RouterProvider router={router}>`
|
|
38
|
+
- route objects with properties like `path`, `children`, `loader`, `action`, `Component`, `ErrorBoundary`, or `lazy`
|
|
39
|
+
- data APIs without the Framework Vite plugin
|
|
40
|
+
|
|
41
|
+
Then read `references/data-mode.md`.
|
|
42
|
+
|
|
43
|
+
### Declarative Mode
|
|
44
|
+
|
|
45
|
+
Use Declarative Mode guidance when you see:
|
|
46
|
+
|
|
47
|
+
- `<BrowserRouter>`, `<HashRouter>`, or `<MemoryRouter>`
|
|
48
|
+
- `<Routes>` and `<Route>` JSX route configuration
|
|
49
|
+
- route components passed with `element={<Component />}`
|
|
50
|
+
- no data router, no route module convention, and no loaders/actions
|
|
51
|
+
|
|
52
|
+
Then read `references/declarative-mode.md`.
|
|
53
|
+
|
|
54
|
+
### RSC Framework and RSC Data Modes
|
|
55
|
+
|
|
56
|
+
React Server Components support is unstable and exists in both Framework and Data variants. Use RSC guidance when you see:
|
|
57
|
+
|
|
58
|
+
- `unstable_reactRouterRSC`
|
|
59
|
+
- `@vitejs/plugin-rsc`
|
|
60
|
+
- `unstable_RSCRouteConfig`
|
|
61
|
+
- RSC entry files such as `entry.rsc`
|
|
62
|
+
- `ServerComponent`, `ServerErrorBoundary`, `ServerLayout`, or `ServerHydrateFallback`
|
|
63
|
+
- React directives or boundary packages such as `"use client"`, `"server-only"`, or `"client-only"`
|
|
64
|
+
|
|
65
|
+
For RSC Framework, read both `references/framework-mode.md` and `references/rsc.md`.
|
|
66
|
+
For RSC Data, read both `references/data-mode.md` and `references/rsc.md`.
|
|
67
|
+
|
|
68
|
+
## Use Installed Docs as Source of Truth
|
|
69
|
+
|
|
70
|
+
React Router ships markdown docs in the package so guidance can match the installed version:
|
|
71
|
+
|
|
72
|
+
```txt
|
|
73
|
+
node_modules/react-router/docs/
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Key docs paths:
|
|
77
|
+
|
|
78
|
+
```txt
|
|
79
|
+
node_modules/react-router/docs/index.md
|
|
80
|
+
node_modules/react-router/docs/start/
|
|
81
|
+
node_modules/react-router/docs/how-to/
|
|
82
|
+
node_modules/react-router/docs/explanation/
|
|
83
|
+
node_modules/react-router/docs/upgrading/
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
When this skill references `react-router/docs/...`, read the matching file under `node_modules/react-router/docs/`. If the installed version does not include local docs, use the repo `docs/` directory when working inside the React Router repository; in a consuming app, fall back to version-matched website docs.
|
|
87
|
+
|
|
88
|
+
Most docs include a mode marker near the top:
|
|
89
|
+
|
|
90
|
+
```txt
|
|
91
|
+
[MODES: framework, data, declarative]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Only apply a doc when its mode marker matches the app mode. If a task spans modes, prefer the section or file that matches the current app.
|
|
95
|
+
|
|
96
|
+
RSC is documented primarily in:
|
|
97
|
+
|
|
98
|
+
```txt
|
|
99
|
+
node_modules/react-router/docs/how-to/react-server-components.md
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Skill References
|
|
103
|
+
|
|
104
|
+
Load the relevant reference after identifying the mode:
|
|
105
|
+
|
|
106
|
+
| Reference | Use When |
|
|
107
|
+
| -------------------------------- | --------------------------------------------- |
|
|
108
|
+
| `references/framework-mode.md` | Framework Mode or RSC Framework base behavior |
|
|
109
|
+
| `references/data-mode.md` | Data Mode or RSC Data base behavior |
|
|
110
|
+
| `references/declarative-mode.md` | Declarative Mode |
|
|
111
|
+
| `references/rsc.md` | Any unstable RSC app |
|
|
112
|
+
|
|
113
|
+
## Mode Migration Doc Index
|
|
114
|
+
|
|
115
|
+
If the user explicitly asks to switch modes, read the target mode reference plus the migration-relevant docs:
|
|
116
|
+
|
|
117
|
+
| Migration | Docs to read |
|
|
118
|
+
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
119
|
+
| Declarative → Data | `react-router/docs/start/modes.md`, `react-router/docs/start/data/routing.md`, `react-router/docs/start/data/data-loading.md`, `react-router/docs/start/data/actions.md` |
|
|
120
|
+
| Declarative/Data → Framework | `react-router/docs/start/modes.md`, `react-router/docs/start/framework/routing.md`, `react-router/docs/start/framework/route-module.md`, `react-router/docs/how-to/route-module-type-safety.md` |
|
|
121
|
+
| Framework SPA/SSR/pre-render changes | `react-router/docs/start/framework/rendering.md`, `react-router/docs/how-to/spa.md`, `react-router/docs/how-to/pre-rendering.md`, `react-router/docs/start/framework/data-loading.md`, `react-router/docs/start/framework/actions.md` |
|
|
122
|
+
| Future flags/upgrades | `react-router/docs/upgrading/future.md` and relevant files under `react-router/docs/upgrading/` |
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Data Mode
|
|
2
|
+
|
|
3
|
+
Data Mode uses data routers such as `createBrowserRouter` and renders with `<RouterProvider>`. It gives an app route objects, loaders, actions, pending UI, fetchers, and SSR primitives without adopting the Framework Vite plugin or route-module file conventions.
|
|
4
|
+
|
|
5
|
+
Use this reference after the main skill identifies a Data Mode app.
|
|
6
|
+
|
|
7
|
+
## Read the Local Docs by Mode
|
|
8
|
+
|
|
9
|
+
Start with:
|
|
10
|
+
|
|
11
|
+
```txt
|
|
12
|
+
react-router/docs/start/modes.md
|
|
13
|
+
react-router/docs/start/data/index.md
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Then use the Data docs under:
|
|
17
|
+
|
|
18
|
+
```txt
|
|
19
|
+
react-router/docs/start/data/
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Those files cover installation, route objects, routing, data loading, actions, navigation, pending UI, and testing. For task-specific details, read relevant files in:
|
|
23
|
+
|
|
24
|
+
```txt
|
|
25
|
+
react-router/docs/how-to/
|
|
26
|
+
react-router/docs/explanation/
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Always check the `[MODES: data, ...]` marker in a doc before applying it.
|
|
30
|
+
|
|
31
|
+
## Data Router Shape
|
|
32
|
+
|
|
33
|
+
Typical setup:
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { createBrowserRouter, RouterProvider } from "react-router";
|
|
37
|
+
|
|
38
|
+
const router = createBrowserRouter([
|
|
39
|
+
{
|
|
40
|
+
path: "/",
|
|
41
|
+
Component: Root,
|
|
42
|
+
loader: rootLoader,
|
|
43
|
+
children: [
|
|
44
|
+
{ index: true, Component: Home },
|
|
45
|
+
{
|
|
46
|
+
path: "projects/:projectId",
|
|
47
|
+
Component: Project,
|
|
48
|
+
loader: projectLoader,
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
root.render(<RouterProvider router={router} />);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Look for route object arrays and APIs such as:
|
|
58
|
+
|
|
59
|
+
- `createBrowserRouter`
|
|
60
|
+
- `createHashRouter`
|
|
61
|
+
- `createMemoryRouter`
|
|
62
|
+
- `RouterProvider`
|
|
63
|
+
- `loader`
|
|
64
|
+
- `action`
|
|
65
|
+
- `Component`
|
|
66
|
+
- `ErrorBoundary`
|
|
67
|
+
- `lazy`
|
|
68
|
+
- `children`
|
|
69
|
+
|
|
70
|
+
## Route Objects and Routing
|
|
71
|
+
|
|
72
|
+
Before editing route configuration, read:
|
|
73
|
+
|
|
74
|
+
```txt
|
|
75
|
+
react-router/docs/start/data/routing.md
|
|
76
|
+
react-router/docs/start/data/route-object.md
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Rules:
|
|
80
|
+
|
|
81
|
+
- Keep route objects outside render when possible.
|
|
82
|
+
- Use nested routes for shared layouts and data boundaries.
|
|
83
|
+
- Use index routes for default child content.
|
|
84
|
+
- Use dynamic segments and splats according to route-object docs.
|
|
85
|
+
- Prefer `Component`/`ErrorBoundary` route object properties in Data Mode examples unless the existing app uses `element` consistently.
|
|
86
|
+
|
|
87
|
+
## Data and Mutations
|
|
88
|
+
|
|
89
|
+
Before working on data loading or mutations, read:
|
|
90
|
+
|
|
91
|
+
```txt
|
|
92
|
+
react-router/docs/start/data/data-loading.md
|
|
93
|
+
react-router/docs/start/data/actions.md
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Rules:
|
|
97
|
+
|
|
98
|
+
- Load route data with route `loader` functions.
|
|
99
|
+
- Mutate route data with route `action` functions.
|
|
100
|
+
- Prefer loaders/actions over route-level `useEffect` fetching.
|
|
101
|
+
- Use `request`, `params`, and returned/throwable Responses as described in the docs.
|
|
102
|
+
- Let React Router revalidate after actions unless there is a documented reason to customize revalidation.
|
|
103
|
+
|
|
104
|
+
Common patterns:
|
|
105
|
+
|
|
106
|
+
- Validation failure from an action: return `data({ errors, values }, { status: 400 })`, then render errors with `useActionData()` or `fetcher.data`.
|
|
107
|
+
- Missing record in a loader: throw `data("Not Found", { status: 404 })` and render the route `ErrorBoundary`.
|
|
108
|
+
- Search/filter data: parse `new URL(request.url).searchParams` in the loader so the URL is shareable and bookmarkable.
|
|
109
|
+
|
|
110
|
+
## Forms, Fetchers, and Pending UI
|
|
111
|
+
|
|
112
|
+
For forms and pending UI, read:
|
|
113
|
+
|
|
114
|
+
```txt
|
|
115
|
+
react-router/docs/start/data/actions.md
|
|
116
|
+
react-router/docs/start/data/pending-ui.md
|
|
117
|
+
react-router/docs/how-to/fetchers.md
|
|
118
|
+
react-router/docs/explanation/form-vs-fetcher.md
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Rules of thumb:
|
|
122
|
+
|
|
123
|
+
- Search/filter form that updates the URL: `<Form method="get">`.
|
|
124
|
+
- Mutation that should change URL/history or redirect after completion: `<Form method="post">`.
|
|
125
|
+
- Mutation that should keep the user on the same page: `useFetcher` / `<fetcher.Form>`.
|
|
126
|
+
- Optimistic UI: derive from `fetcher.formData` or `navigation.formData`.
|
|
127
|
+
|
|
128
|
+
## Navigation and URL State
|
|
129
|
+
|
|
130
|
+
Before changing navigation or search params, read:
|
|
131
|
+
|
|
132
|
+
```txt
|
|
133
|
+
react-router/docs/start/data/navigating.md
|
|
134
|
+
react-router/docs/how-to/search-params.md
|
|
135
|
+
react-router/docs/explanation/location.md
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Rules:
|
|
139
|
+
|
|
140
|
+
- Use `<Link>`/`<NavLink>` for user-initiated internal navigation.
|
|
141
|
+
- Use `redirect` in loaders/actions when navigation follows data loading or mutations.
|
|
142
|
+
- Use `useNavigate` for imperative client-side event navigation.
|
|
143
|
+
- Treat URL params as strings and validate/parse them.
|
|
144
|
+
- Preserve unrelated search params unless intentionally resetting them.
|
|
145
|
+
|
|
146
|
+
## SSR in Data Mode
|
|
147
|
+
|
|
148
|
+
Data Mode SSR is manual and lower-level than Framework Mode. Before implementing or changing SSR, read the Data Mode custom/SSR docs and match existing server abstractions.
|
|
149
|
+
|
|
150
|
+
Start with:
|
|
151
|
+
|
|
152
|
+
```txt
|
|
153
|
+
react-router/docs/start/data/custom.md
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Look for APIs like `createStaticHandler`, `createStaticRouter`, `StaticRouterProvider`, and hydration data handling in the current app before changing anything.
|
|
157
|
+
|
|
158
|
+
## RSC Data
|
|
159
|
+
|
|
160
|
+
If this Data Mode app uses `unstable_RSCRouteConfig`, RSC route config, or low-level RSC server APIs, also read:
|
|
161
|
+
|
|
162
|
+
```txt
|
|
163
|
+
references/rsc.md
|
|
164
|
+
react-router/docs/how-to/react-server-components.md
|
|
165
|
+
```
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Declarative Mode
|
|
2
|
+
|
|
3
|
+
Declarative Mode is React Router's simplest mode. It uses router components like `<BrowserRouter>` and JSX routes with `<Routes>`/`<Route>`. It does not provide loaders, actions, fetchers, or data-router pending UI.
|
|
4
|
+
|
|
5
|
+
Use this reference after the main skill identifies a Declarative Mode app.
|
|
6
|
+
|
|
7
|
+
## Read the Local Docs by Mode
|
|
8
|
+
|
|
9
|
+
Start with:
|
|
10
|
+
|
|
11
|
+
```txt
|
|
12
|
+
react-router/docs/start/modes.md
|
|
13
|
+
react-router/docs/start/declarative/index.md
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Then use the Declarative docs under:
|
|
17
|
+
|
|
18
|
+
```txt
|
|
19
|
+
react-router/docs/start/declarative/
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Those files cover installation, routing, navigation, and URL values. For conceptual details, read relevant files in:
|
|
23
|
+
|
|
24
|
+
```txt
|
|
25
|
+
react-router/docs/explanation/
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Always check the `[MODES: declarative, ...]` marker in a doc before applying it.
|
|
29
|
+
|
|
30
|
+
## Declarative Router Shape
|
|
31
|
+
|
|
32
|
+
Typical setup:
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import { BrowserRouter, Routes, Route } from "react-router";
|
|
36
|
+
|
|
37
|
+
function App() {
|
|
38
|
+
return (
|
|
39
|
+
<BrowserRouter>
|
|
40
|
+
<Routes>
|
|
41
|
+
<Route path="/" element={<Home />} />
|
|
42
|
+
<Route path="about" element={<About />} />
|
|
43
|
+
<Route path="dashboard" element={<DashboardLayout />}>
|
|
44
|
+
<Route index element={<DashboardHome />} />
|
|
45
|
+
<Route path="settings" element={<Settings />} />
|
|
46
|
+
</Route>
|
|
47
|
+
</Routes>
|
|
48
|
+
</BrowserRouter>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Look for APIs such as:
|
|
54
|
+
|
|
55
|
+
- `<BrowserRouter>`
|
|
56
|
+
- `<HashRouter>`
|
|
57
|
+
- `<MemoryRouter>`
|
|
58
|
+
- `<Routes>`
|
|
59
|
+
- `<Route>`
|
|
60
|
+
- `element={<Component />}`
|
|
61
|
+
- `useRoutes`
|
|
62
|
+
|
|
63
|
+
## Routing
|
|
64
|
+
|
|
65
|
+
Before editing routes, read:
|
|
66
|
+
|
|
67
|
+
```txt
|
|
68
|
+
react-router/docs/start/declarative/routing.md
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Rules:
|
|
72
|
+
|
|
73
|
+
- Use `<Routes>` and `<Route>` for route configuration.
|
|
74
|
+
- Use nested routes with `<Outlet>` for shared layout.
|
|
75
|
+
- Use index routes for default child UI.
|
|
76
|
+
- Use route params and splats according to the declarative routing docs.
|
|
77
|
+
- Do not add route object loaders/actions to a Declarative router.
|
|
78
|
+
|
|
79
|
+
## Navigation
|
|
80
|
+
|
|
81
|
+
Before changing navigation, read:
|
|
82
|
+
|
|
83
|
+
```txt
|
|
84
|
+
react-router/docs/start/declarative/navigating.md
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Rules:
|
|
88
|
+
|
|
89
|
+
- Use `<Link>` or `<NavLink>` for user-initiated internal navigation.
|
|
90
|
+
- Use `NavLink` when active styling matters.
|
|
91
|
+
- Use `useNavigate` for imperative navigation from event handlers or effects.
|
|
92
|
+
- Do not use plain `<a href>` for internal navigation unless intentionally forcing a full document navigation.
|
|
93
|
+
|
|
94
|
+
## URL Values
|
|
95
|
+
|
|
96
|
+
Before changing params, search params, or location state, read:
|
|
97
|
+
|
|
98
|
+
```txt
|
|
99
|
+
react-router/docs/start/declarative/url-values.md
|
|
100
|
+
react-router/docs/explanation/location.md
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Rules:
|
|
104
|
+
|
|
105
|
+
- Use `useParams` for dynamic route params.
|
|
106
|
+
- Use `useSearchParams` for query string state.
|
|
107
|
+
- Use `useLocation` for the current location object and navigation state.
|
|
108
|
+
- Validate and parse URL params; they are strings and can be absent.
|
|
109
|
+
- Preserve unrelated search params unless intentionally resetting them.
|
|
110
|
+
|
|
111
|
+
## Mode Boundary
|
|
112
|
+
|
|
113
|
+
Declarative Mode does not have Data/Framework APIs such as:
|
|
114
|
+
|
|
115
|
+
- `loader`
|
|
116
|
+
- `action`
|
|
117
|
+
- `<Form>`
|
|
118
|
+
- `useFetcher`
|
|
119
|
+
- `useNavigation`
|
|
120
|
+
- route module exports
|
|
121
|
+
- generated `./+types` route types
|
|
122
|
+
|
|
123
|
+
If the user asks for route data loading, DB/API-backed data, CRUD, form mutations, validation returned from submissions, revalidation, pending UI, optimistic UI, or fetchers, recommend Data Mode or Framework Mode depending on how much structure they want. Ask before migrating unless they already requested it.
|