Next.js
How midcode runs a Next.js project, how each element learns where it's written, and what you can edit, including pages, site settings and the free canvas.
Next.js is the stack midcode edits most completely. Text, styles, images, layout, components with their variants and states, new pages, site settings and the free canvas outside the breakpoints: each one is written into your .tsx and .jsx files. Nothing is installed in the project and next.config is never touched.
The full set needs the App Router. A project that only has the Pages Router is edited on the canvas the same way and its pages are listed, but new pages and Site settings are written for the App Router. The free canvas comes to the Pages Router in the next release.
At a glance
| Next.js | |
|---|---|
| Detected by | next in the dependencies or devDependencies of package.json |
| Runs with | next dev, started by midcode with one --require in front |
| Elements are marked by | A loader added in memory to Turbopack (or webpack) when the dev server starts |
| Editing | Full, in .tsx and .jsx files |
| Components | Yes: instances, props, variants and states, variables |
| Pages | app/**/page.* and pages/**. New pages and removing them in the App Router |
| Styles | Tailwind 4 classes, or mid: classes with midcode.css imported in the root layout |
| Tried with | Next.js 16 on Turbopack (16.3 among them), with Tailwind 4 and without Tailwind |
How midcode runs it
When you open a folder, midcode reads its package.json. If next is in dependencies or devDependencies, the project is Next.js. That check comes before Vite, Astro and the others, so a Next.js project that also has vite installed is still Next.js.
If the dependencies aren’t installed, the canvas says “Dependencies are missing” and offers “Install with pnpm” (or npm, yarn, bun: the one your lockfile belongs to).
Then midcode starts the dev server itself. It doesn’t run your dev script:
node --require /Applications/midcode.app/Contents/Resources/injected/next-hook.cjs \
node_modules/next/dist/bin/next dev --port 4310 --hostname localhostnodeis the one on your login shell’s PATH: midcode asks your shell, so a version manager’s Node is found. If the Mac has no Node at all, midcode’s own stands in.nextis your project’s own, found the way Node resolves it (so a hoisted install works).The port is the first free one from 4310 up.
The dev server log (the button with the status dot, on the right of the top bar) starts with that command and the Node that runs it: $ next dev --port 4310 (/opt/homebrew/bin/node).
The --require is all midcode adds. It preloads a hook into next dev and into the processes Next forks. The hook waits for Next to load your config and, on the object Next hands back, in memory, adds three things:
a Turbopack rule for
*.tsxand*.jsxthat runs midcode’s loader first. Rules you already have for those files still run after it.the same loader as a webpack rule, when Next runs on webpack (its default up to Next 15). Your own
webpack()function runs first.devIndicators: false, so Next’s dev badge doesn’t sit on top of the canvas.
Nothing is written to disk: not next.config, not package.json, not the lockfile. The marks exist only in the dev server midcode started. next build, a next dev you run in a terminal and your deployed site have none. Telemetry is also off for that one process (NEXT_TELEMETRY_DISABLED=1). What midcode does add to a project, and when, is listed in What midcode adds to your project.
If midcode quits or crashes, the dev server notices within a few seconds and stops itself, so the port and Next’s lock are free again.
What the loader adds
The loader runs on every .tsx and .jsx file outside node_modules. It adds attributes to the compiled output and leaves everything else byte for byte where it was, so line numbers stay true.
import { Card } from '@/components/Card'
export default function Page() {
return (
<main className="mx-auto max-w-3xl px-6 py-24">
<h1 className="text-5xl font-semibold">Selected work</h1>
<Card title="Oak table" />
</main>
)
}export function Card({ title }: { title: string }) {
return (
<article className="rounded-2xl p-6">
<h3>{title}</h3>
</article>
)
}This is what reaches the page:
<main data-mc="app/page.tsx:5:5" class="mx-auto max-w-3xl px-6 py-24">
<h1 data-mc="app/page.tsx:6:7" class="text-5xl font-semibold">Selected work</h1>
<article data-mc="components/Card.tsx:3:5" data-mcu="Card|app/page.tsx:7:7" class="rounded-2xl p-6">
<h3 data-mc="components/Card.tsx:4:7">Oak table</h3>
</article>
</main>data-mcgoes on every element written in your JSX: the file (relative to the project), the line and the column of its<. A wrapped element like<motion.div>counts as an element.data-mciis handed to every component instance as a prop:Card|app/page.tsx:7:7, the name and where it’s used. It only shows in the page when the component passes its props on to an element.data-mcugoes on the element a component returns, and says where that component is used. Nested components make a chain, outermost first, separated by spaces:Hero|app/page.tsx:9:7 Section|components/Hero.tsx:4:5. This is how Layers names components and how the root of a component is moved where it’s used.
Fragments, Suspense, StrictMode, Profiler, providers and consumers get no mark: they draw no element of their own.
Server and client components are marked alike: the loader works on the source file, and so do the edits.
What you can edit
Click the <h1> and the right panel shows where it’s written under Code. Change its size and one class is swapped in that file:
- <h1 className="text-5xl font-semibold">Selected work</h1>+ <h1 className="text-6xl font-semibold">Selected work</h1>Everything in the editor works in a Next.js project:
Text, edited in place, including text that lives in an array or a content file: see Text, images and video.
Position, size, layout, type, fill and effects, per breakpoint: see The style panel.
Images and video. The new file is copied into
public/and the reference rewritten. An image your code imports (import hero from './hero.jpg') is replaced in place, with a file of the same type.Structure: Insert, and moving, duplicating and deleting. Interactive components from Insert are written once to
components/midcode/(src/components/midcode/when your app lives insrc).Components with variants, states and typed props, and Variables.
Every edit is one step of ⌘Z. What makes an element editable, and what doesn’t, is in Code that midcode can edit.
Pages
The page menu in the top bar lists what midcode finds in src/app, app, src/pages and pages.
| File | Page |
|---|---|
app/page.tsx | / |
app/(marketing)/pricing/page.tsx | /pricing |
app/work/[slug]/page.tsx | /work/[slug], a dynamic route |
pages/about.tsx | /about |
pages/blog/index.tsx | /blog |
Pages can be .tsx, .ts, .jsx, .js or .mdx. Left out: api folders, folders that start with _ or @, and in pages/ the files that start with _ (_app, _document). A dynamic route is one entry, and its pages are found in the running site (its sitemap and the links on its pages), then in the page’s own generateStaticParams.
In an App Router project you can also make and remove pages. “New page” writes a page.tsx in a new folder, next to the closest parent page that exists:
export default function Page() {
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>
)
}“Remove page” moves the file to the Trash, and its folder too when the page was the only thing in it. Reordering, dynamic routes and the Assets tab are in Pages and navigation.
Site settings
The globe in the top bar opens Site settings. In the released version it only shows in Next.js projects. Title, description, search engines, favicon and social image are written the way Next.js reads them: export const metadata in the root layout or in the page, generateMetadata when a dynamic page’s title uses its item’s fields, and image files by convention (app/icon.*, app/opengraph-image.*). A change replaces only the value:
export const metadata: Metadata = {- title: 'Create Next App',+ title: 'Oak & Iron', description: 'Furniture made to order.', }It needs a root layout (app/layout.tsx). The details are in Site settings. In the next release other kinds of project get the same form, written as tags of their <head>: Next.js keeps writing metadata.
The free canvas
In an App Router project, anything you draw (F), drop or paste on the empty canvas floats there. It lives in one real page, app/midcode-scratch/page.tsx (under src/app if that’s where your app is), that answers 404 in production, stays out of the page list and is never offered in Publish. See The free canvas.
Styles
A project counts as Tailwind 4 when one of its CSS files has @import "tailwindcss" or an @theme block. There, midcode writes Tailwind’s own classes and reads your @theme tokens: see Tailwind CSS.
In any other Next.js project (plain CSS, CSS Modules, Tailwind 3) midcode writes its own prefixed utilities, like mid:p-6, and compiles them to midcode.css. On your first style edit that file is created next to the root layout and imported there, as one undoable edit:
import type { Metadata } from 'next' import './globals.css'+import './midcode.css'Without a root layout the import goes in pages/_app.*. If midcode finds neither, it still writes midcode.css and tells you to import it yourself. See Without Tailwind.
Limits
Only
.tsxand.jsxfiles are marked. JSX written in a.jsfile, common in Next.js projects without TypeScript, gets no marks: its elements show, but midcode can’t tell where they’re written until the file is renamed to.jsx. The content of.mdxpages isn’t marked either.midcode runs
next devitself. Flags in yourdevscript (--turbopack,--webpack,--experimental-https, a port) aren’t read, and a custom server (node server.js) isn’t used. In the next release you can give midcode your own command (see Any other stack); the site then shows without marks.There is no fallback to your own script, as there is for Vite projects. If
next devdoesn’t start, the canvas shows the error.On Next 15 and older, a dev server started this way runs on webpack. The loader is written for it, but only Next 16 on Turbopack is on record as tried.
With the Pages Router only: no Site settings, which needs a root layout in
app/, and in the released version no free canvas (the next release keeps it inpages/midcode-scratch.tsx). New page is still offered, and writesapp/<path>/page.tsx: an App Router page beside yourpages/folder.A new page starts with Tailwind class names. In a project without Tailwind 4 they do nothing until you restyle it.
An element drawn by a package (a UI library in
node_modules) has no mark. The right panel says a library draws it: edit its parent, or the component that uses it.An element moves within its file and its function. Text and classes that are computed are edited in code.
Troubleshooting
“A Next server is already running for this project”. Next allows one dev server per project folder, and midcode needs its own. Click “Stop it and use midcode”, or stop yours in the terminal and press Restart in the dev server log.
“Next.js is not in node_modules. Install the dependencies.” Use the “Install with…” button on the same card.
An element has no file and line in the right panel. It has no mark. Check that its file is .tsx or .jsx, and that it isn’t drawn by a package. If nothing in the project has one, the hook couldn’t attach to this version of Next: the dev server then runs as it always would, without marks. Send the report from the dev server log (“Send report”).
A style doesn’t show. In a project without Tailwind 4, check that midcode.css is imported in the root layout.
More in Troubleshooting and How midcode works.