# 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.

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

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](https://midcode.app/docs/start/open-a-project.md), 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:

```bash
node --require /Applications/midcode.app/Contents/Resources/injected/next-hook.cjs \
  node_modules/next/dist/bin/next dev --port 4310 --hostname localhost
```

- `node` is 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.
- `next` is 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 `*.tsx` and `*.jsx` that 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](https://midcode.app/docs/start/project-files.md).

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.

```tsx title="app/page.tsx"
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>
  )
}
```

```tsx title="components/Card.tsx"
export function Card({ title }: { title: string }) {
  return (
    <article className="rounded-2xl p-6">
      <h3>{title}</h3>
    </article>
  )
}
```

This is what reaches the page:

```html
<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-mc` goes 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-mci` is 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-mcu` goes 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](https://midcode.app/docs/editor/layers.md) 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:

```diff title="app/page.tsx"
-      <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](https://midcode.app/docs/editor/text-and-media.md).
- Position, size, layout, type, fill and effects, per breakpoint: see [The style panel](https://midcode.app/docs/editor/styles.md).
- 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](https://midcode.app/docs/editor/insert.md), and [moving, duplicating and deleting](https://midcode.app/docs/editor/select-move-resize.md). Interactive components from Insert are written once to `components/midcode/` (`src/components/midcode/` when your app lives in `src`).
- [Components](https://midcode.app/docs/editor/components.md) with variants, states and typed props, 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) and [Publish](https://midcode.app/docs/publish/publish.md).

Every edit is one step of `⌘Z`. What makes an element editable, and what doesn't, is in [Code that midcode can edit](https://midcode.app/docs/agents/editable-code.md).

## 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:

```tsx title="app/about/page.tsx"
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](https://midcode.app/docs/editor/pages.md).

## 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:

```diff title="app/layout.tsx"
 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](https://midcode.app/docs/editor/site-settings.md). 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](https://midcode.app/docs/editor/free-canvas.md).

## 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](https://midcode.app/docs/styling/tailwind.md).

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:

```diff title="app/layout.tsx"
 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](https://midcode.app/docs/styling/without-tailwind.md).

## Limits

- Only `.tsx` and `.jsx` files are marked. JSX written in a `.js` file, 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 `.mdx` pages isn't marked either.
- midcode runs `next dev` itself. Flags in your `dev` script (`--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](https://midcode.app/docs/frameworks/custom-server.md)); the site then shows without marks.
- There is no fallback to your own script, as there is for Vite projects. If `next dev` doesn'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 in `pages/midcode-scratch.tsx`). New page is still offered, and writes `app/<path>/page.tsx`: an App Router page beside your `pages/` 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](https://midcode.app/docs/reference/troubleshooting.md) and [How midcode works](https://midcode.app/docs/start/how-it-works.md).
