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.
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.
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):
react-router dev /Users/you/apps/store \
--config "/Users/you/Library/Application Support/midcode/injected/vite.config.mjs" \
--port 4310 --strictPort --host localhostFor 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.
Loaders and actions run as usual: the canvas shows your real app from your real dev server.
What the plugin adds
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:
<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:
- <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. New media goes to
public/.The style panel, per breakpoint.
Insert, and moving, duplicating and deleting. Interactive components are written once to
app/components/midcode/.Components and Variables.
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:
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 RouteConfigThat 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.
New routesNext 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:
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>
)
}-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.
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:
import type { Route } from "./+types/root"; import "./app.css";+import './midcode.css'See Without Tailwind.
Limits
No Site settings and no free canvas. The
<head>of these apps is JSX inapp/root.tsx, and each route’smeta: 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 anapp/routes/folder (Remix): without it the plus isn’t in the page menu. Whenapp/routes.tsbuilds its list another way than[…] satisfies RouteConfig(withflatRoutes(), 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.jsonin the project replaces midcode’s own start: the site shows without marks (Any other stack).Nesting isn’t followed in
routes.ts. A route insideprefix('blog', […]), or nested under anotherroute(…), 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_indexis 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
.tsxand.jsxfiles 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.