# React Router and Remix

> How midcode runs React Router in framework mode and Remix on Vite, how your routes become its pages, and what you can edit in their JSX.

- Page: https://midcode.app/docs/frameworks/react-router
- From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt

midcode edits React Router apps (framework mode) and Remix apps that run on Vite the way it edits any React project: text, styles, images, structure, components and variables, written into your route and component files. On top of that it reads your routes, so each one is a page you can go to from the top bar.

In the released version routes are made in code. The next release adds and removes them from the page menu. Editing `meta` from the app isn't there: these apps write their `<head>` in JSX, which Site settings doesn't write. A Remix app on the classic compiler, before Vite, opens to look at and comment on, without editing.

## At a glance

| | React Router and Remix |
| --- | --- |
| Detected by | `@react-router/dev` (React Router) or `@remix-run/dev` (Remix) in `package.json` |
| Runs with | `react-router dev` or `remix vite:dev`, with a Vite config of midcode's that loads yours and adds one plugin |
| Elements are marked by | That plugin, as Vite transforms each `.tsx` and `.jsx` file |
| Editing | Full, in `.tsx` and `.jsx` files |
| Components | Yes: instances, props, variants and states, variables |
| Pages | `app/routes.ts`, or the files of `app/routes/`. New routes and removing them in the next release |
| Styles | Tailwind 4 classes, or `mid:` classes with `midcode.css` imported in `app/root.tsx` |
| Tried with | A React Router project made by `create-react-router`, with Tailwind 4 |

React Router used as a library inside a plain Vite app (`react-router` or `react-router-dom`, without `@react-router/dev`) is a Vite project: see [React with Vite](https://midcode.app/docs/frameworks/react-vite.md).

## How midcode runs it

midcode reads `package.json`. `@react-router/dev` makes the project React Router, `@remix-run/dev` makes it Remix. Both are checked before plain Vite.

It then starts the framework's own dev command, from your `node_modules`, with the `node` of your login shell (midcode's own Node if the Mac has none):

```bash
react-router dev /Users/you/apps/store \
  --config "/Users/you/Library/Application Support/midcode/injected/vite.config.mjs" \
  --port 4310 --strictPort --host localhost
```

For Remix the command is `remix vite:dev`, with the same options. The port is the first free one from 4310 up.

The `--config` points at a file in midcode's own data folder, outside your project. It loads your `vite.config` with your project's own Vite and returns it with one more plugin at the front. Your config, with its `reactRouter()` or `remix()` plugin, runs as it always does. Nothing is written in the project, and the plugin only exists in the dev server midcode started. The file itself is shown in [React with Vite](https://midcode.app/docs/frameworks/react-vite.md).

Loaders and actions run as usual: the canvas shows your real app from your real dev server.

### What the plugin adds

```tsx title="app/routes/home.tsx"
import { Welcome } from '../welcome/welcome'

export default function Home() {
  return (
    <main className="mx-auto max-w-3xl px-6 py-24">
      <h2>Our work</h2>
      <Welcome />
    </main>
  )
}
```

In the page, the heading arrives with the file, line and column it's written at:

```html
<h2 data-mc="app/routes/home.tsx:6:7">Our work</h2>
```

`data-mc` goes on every element of your JSX. A component instance gets `data-mci` as a prop (`Welcome|app/routes/home.tsx:7:7`), and the element that component returns carries the same value in `data-mcu`, which is how midcode knows where a component is used.

Double-click the heading, type, and that string is the only thing that changes:

```diff title="app/routes/home.tsx"
-      <h2>Our work</h2>
+      <h2>Selected work</h2>
```

### When midcode's way doesn't start

If the dev command started this way exits, or doesn't answer in 45 seconds, midcode stops it and runs your own `dev` script instead. The site shows, without marks: nothing can be edited on the canvas. A message says so when the canvas appears, with a "Send report" button, and the dev server log says why.

This is what happens with a Remix app on the classic compiler: `remix vite:dev` has no Vite to start.

## What you can edit

Everything the editor does with JSX:

- [Text, images and video](https://midcode.app/docs/editor/text-and-media.md). New media goes to `public/`.
- [The style panel](https://midcode.app/docs/editor/styles.md), per breakpoint.
- [Insert](https://midcode.app/docs/editor/insert.md), and [moving, duplicating and deleting](https://midcode.app/docs/editor/select-move-resize.md). Interactive components are written once to `app/components/midcode/`.
- [Components](https://midcode.app/docs/editor/components.md) and [Variables](https://midcode.app/docs/editor/variables.md).
- [CMS](https://midcode.app/docs/data/cms.md), [Languages](https://midcode.app/docs/data/languages.md), [Code view](https://midcode.app/docs/editor/code.md), [Publish](https://midcode.app/docs/publish/publish.md).

## Pages

midcode reads routes from the source, without running anything. It looks in `app/` (or `src/app/`).

If there's a `routes.ts` (or `.js`, `.tsx`), each `index(…)` and `route(…)` in it is a page:

```ts title="app/routes.ts"
import { type RouteConfig, index, route } from '@react-router/dev/routes'

export default [
  index('routes/home.tsx'),
  route('about', 'routes/about.tsx'),
  route('work/:slug', 'routes/project.tsx'),
  route('files/*', 'routes/files.tsx'),
] satisfies RouteConfig
```

That gives `/`, `/about`, `/work/[slug]` and `/files/[...]`. The last two are dynamic routes: one entry each, whose pages midcode finds in the running site (its sitemap and the links on its pages).

Without a `routes.ts` (Remix), or when it only calls `flatRoutes()`, the files of `app/routes/` are the pages:

| File | Page |
| --- | --- |
| `_index.tsx` | `/` |
| `about.tsx` | `/about` |
| `work._index.tsx` | `/work` |
| `work.$slug.tsx` | `/work/[slug]` |
| `docs/route.tsx` | `/docs` |
| `$.tsx` | `/[...]` |

A page that isn't listed can still be opened: type its path in the page menu and press `Enter`. More in [Pages and navigation](https://midcode.app/docs/editor/pages.md).

### New routes (next release)

In the next release, "New page" in the page menu makes a route, and "Remove" takes one out.

In React Router, a new page at `/about` is a file in `app/routes/` and its line in `app/routes.ts`. `route` is added to the import when it isn't there:

```tsx title="app/routes/about.tsx"
export default function About() {
  return (
    <main className="mx-auto max-w-3xl px-6 py-24">
      <h1 className="text-4xl font-semibold">About</h1>
      <p className="mt-4">A new page, made in midcode.</p>
    </main>
  )
}
```

```diff title="app/routes.ts"
-import { type RouteConfig, index } from "@react-router/dev/routes";
+import { type RouteConfig, index, route } from "@react-router/dev/routes";
 
-export default [index("routes/home.tsx")] satisfies RouteConfig;
+export default [index("routes/home.tsx"), route("about", "routes/about.tsx")] satisfies RouteConfig;
```

The new line goes right after the last route of the list, on the same line as it. A deeper path uses dots in the file's name: `/blog/first` is `app/routes/blog.first.tsx`. The class names are written when Tailwind is in your `package.json`; without it the tags come bare.

In Remix the file is the route, so only `app/routes/about.tsx` is written.

"Remove" moves the route's file to the Trash and, in React Router, takes its `route(…)` line out of `app/routes.ts` (and `route` out of the import, when that was its last use).

Creating and removing was tried on both. The new page was looked at on the canvas in React Router, not in a running Remix app.

## Styles

With Tailwind 4, midcode writes Tailwind's classes: see [Tailwind CSS](https://midcode.app/docs/styling/tailwind.md).

Without it, midcode writes `mid:` utilities and compiles them to `midcode.css`, created next to `root.tsx` on your first style edit and imported there:

```diff title="app/root.tsx"
 import type { Route } from "./+types/root";
 import "./app.css";
+import './midcode.css'
```

See [Without Tailwind](https://midcode.app/docs/styling/without-tailwind.md).

## Limits

- No Site settings and no free canvas. The `<head>` of these apps is JSX in `app/root.tsx`, and each route's `meta`: write them in code, or ask your agent.
- In the released version, routes are made in code. In the next release a new route needs an `app/routes.ts` (React Router) or an `app/routes/` folder (Remix): without it the plus isn't in the page menu. When `app/routes.ts` builds its list another way than `[…] satisfies RouteConfig` (with `flatRoutes()`, say), midcode answers "midcode can't add a page to this project by itself yet: its pages are made in code. Ask the agent for it."
- In the next release, a `.midcode/server.json` in the project replaces midcode's own start: the site shows without marks ([Any other stack](https://midcode.app/docs/frameworks/custom-server.md)).
- Nesting isn't followed in `routes.ts`. A route inside `prefix('blog', […])`, or nested under another `route(…)`, is listed without its parent's path. `layout(…)` adds nothing, which is right.
- In `app/routes/`, a file that starts with an underscore other than `_index` is skipped. That leaves out pathless layouts, as it should, but also the routes named under them (`_auth.login.tsx`). Routes with a trailing underscore segment (`blog_.$slug.tsx`) are skipped too.
- Only `.tsx` and `.jsx` files are marked.
- An image your code imports isn't swapped from the canvas. An image referenced by its path is.
- The dev server listens on midcode's port, not the one in your config.
- Remix hasn't been tried with a real project, and styling without Tailwind hasn't been tried on React Router or Remix. Both run on the same code as Vite + React, where it has.

## Troubleshooting

**The site shows but nothing can be edited.** midcode fell back to your `dev` script. The dev server log (the button with the status dot in the top bar) says why on the line before the second start.

**"React Router is not in node_modules. Install the dependencies."** (or "Remix"). Install with the button on the card.

**A route is missing from the page menu.** It's one of the cases under Limits. Type its path in the menu.

More in [Troubleshooting](https://midcode.app/docs/reference/troubleshooting.md).
