# Nuxt

> How midcode starts a Nuxt dev server with a layer of its own, which files are pages, how auto-imported components get their props, and where midcode.css goes.

- Page: https://midcode.app/docs/frameworks/nuxt
- From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt
- Status: This ships with the next release of midcode. The version you can download today (1.1.2) doesn't have it yet.

midcode edits a Nuxt project in the templates of its pages, layouts and components: text, classes, attributes, new elements, their order, and the props a component is given where it's used. It starts Nuxt's own dev server with one extra layer, so your `nuxt.config` is never changed. midcode 1.1.2 and earlier open a Nuxt project to check every breakpoint and leave comments, without editing.

Nuxt's files are Vue single-file components, and midcode edits them the same way as in any Vue app. This page covers what's particular to Nuxt; [Vue](https://midcode.app/docs/frameworks/vue.md) shows each kind of edit with the code it writes.

## At a glance

| | Nuxt |
| --- | --- |
| Detected by | `nuxt` in the dependencies of `package.json` |
| Runs with | `nuxi dev`, extended with a layer of midcode's |
| Elements are marked by | A Vite plugin that layer adds, while the dev server runs |
| Editing | Text, classes, attributes, tag; insert, move, duplicate, delete, wrap in a stack |
| Components | Instances and their props, auto-imported components included. A component opens on its own canvas, with variants, states and variables |
| Pages | The `.vue` files of `pages/` or `app/pages/`. New pages and removing them, when that folder exists |
| Styles | Your Tailwind 4 classes, or `mid:` utilities with `midcode.css` imported from `app.vue` |
| Tried with | Nuxt 4 |

## How midcode runs it

A project is Nuxt when `nuxt` is in its dependencies. midcode looks in your `node_modules` for Nuxt's command line (the `nuxi` package, then `@nuxt/cli`, then `nuxt` itself) and runs it:

```bash
nuxi dev --port 4310 --host localhost --extends <midcode's layer>
```

The port is the first free one from 4310, and the process runs with the Node your shell has, or with midcode's own when there is none.

The layer is a folder in midcode's own data folder (`~/Library/Application Support/midcode/injected/nuxt-layer/`), with one file in it:

```js title="nuxt.config.mjs (midcode's layer, outside your project)"
// Written by midcode: a Nuxt layer that adds the midcode plugin to Vite.
import { midcode } from 'file:///…/vite-plugin.mjs'
export default { vite: { plugins: [midcode(process.env.MIDCODE_ROOT)] } }
```

Nuxt merges a layer under your own config, so your `nuxt.config`, your modules and your `package.json` stay as they are. The plugin only applies to the dev server.

As each `.vue` file is loaded, the plugin marks the elements of its `<template>` before Vue compiles it: `data-mc="app/pages/index.vue:3:5"` (file, line and column) on every element, and `data-mci="Card|app/pages/index.vue:8:5"` on every component used there. Nothing is written to disk. [Vue](https://midcode.app/docs/frameworks/vue.md) shows a template before and after.

Nuxt's own pass-through components get no instance mark, because an attribute on them would land on a whole page: `<NuxtPage>`, `<NuxtLayout>`, `<NuxtLoadingIndicator>`, `<NuxtRouteAnnouncer>`, `<ClientOnly>`, `<DevOnly>` and `<ServerOnly>`.

If the server doesn't come up this way (the process stops, or nothing answers in 45 seconds), midcode runs your own `dev` script instead and says so: the site shows, without marks, and you can only view and comment. The same happens when Nuxt's command line isn't found in `node_modules`.

## What you can edit

Everything on the canvas works on the template of the file the element is written in: a page, a layout, `app.vue` or a component.

- **Text**: double-click and type. Text that's written in the template is replaced there. Text that comes from `{{ }}` is looked for as an exact string in the project's `.ts`, `.js`, `.json` and `.md` files and edited there when midcode can tell which one it is ([Text, images and video](https://midcode.app/docs/editor/text-and-media.md)).
- **Styles**: the [style panel](https://midcode.app/docs/editor/styles.md) writes into the static `class` attribute. A `:class` binding is left alone.
- **Attributes**: links, images, inputs, a `<select>` and its options. A value that's code is written the way Vue binds it (`:maxlength="40"`).
- **Structure**: [Insert](https://midcode.app/docs/editor/insert.md), drag to move within the same file (`⌥`-drag to leave a copy there instead), duplicate, delete, and wrap several elements in a stack with `⇧A`.

A style edit on a page looks like this:

```diff title="app/pages/index.vue"
 <template>
-  <h1 class="text-4xl font-medium">Studio</h1>
+  <h1 class="text-5xl font-medium">Studio</h1>
 </template>
```

### Auto-imported components

Nuxt lets a template use a component without importing it. When midcode finds no import for `<BaseButton>` in the file, it looks for a `.vue` file by that name under `components/`, `app/components/`, `src/components/` and `layers/`, up to five folders deep. It follows Nuxt's naming: `components/base/Button.vue` is `<BaseButton>`, and so is `components/base/BaseButton.vue`.

Once the file is found, the instance's **Props** are typed controls, read from the component's `defineProps` (a type argument with `withDefaults` or a destructuring, an object of constructors, or an array of names). A change is written on the instance:

```diff title="app/pages/index.vue"
-    <BaseButton label="Start" />
+    <BaseButton label="Start now" :size="2" outline />
```

A text is written as `label="…"`, a number or other code as `:size="…"`, a switch that's on as the bare name. A value equal to the declared default removes the attribute.

A component Nuxt or a module provides (`<NuxtLink>`) has no file in your project, so midcode lists what the instance is given, without the component's own types.

A component of your own is in the Assets tab under the name Nuxt gives it (`components/base/Button.vue` is `BaseButton`), and opens on its own canvas, where you add variants and states. They're written as in any Vue component: see [Variants and states](https://midcode.app/docs/frameworks/vue.md). Pages, layouts, `app.vue` and `error.vue` aren't listed as components.

## Pages

Pages are the `.vue` files of `pages/` and `app/pages/`, whichever your project has:

| File | Page |
| --- | --- |
| `app/pages/index.vue` | The home page |
| `app/pages/about.vue` | `/about` |
| `app/pages/blog/index.vue` | `/blog` |
| `app/pages/blog/[slug].vue` | `/blog/[slug]`, a dynamic route |

For a dynamic route, the page menu in the top bar lists its real pages, found in the running site's sitemap and links ([Pages and navigation](https://midcode.app/docs/editor/pages.md)). Folders whose name starts with `_` or a dot aren't listed, and a folder in parentheses doesn't count in the path. A project with no pages folder shows the home page alone.

When the project has a pages folder, "New page" in the page menu writes a page in it, and "Remove" moves a page's file to the Trash:

```vue title="app/pages/about.vue"
<template>
  <main class="mx-auto max-w-3xl px-6 py-24">
    <h1 class="text-4xl font-semibold">About</h1>
    <p class="mt-4">A new page, made in midcode.</p>
  </main>
</template>
```

The class names are written when Tailwind is in your `package.json`; without it the tags come bare. Without a pages folder the plus isn't in the menu: Nuxt only draws pages once there is one and `app.vue` has a `<NuxtPage />`, so the first page is yours or your agent's to set up.

Nuxt keeps the site's `<head>` in its config, so that's where [Site settings](https://midcode.app/docs/editor/site-settings.md) reads and writes: `app.head.title`, `app.head.htmlAttrs.lang`, the `description`, `robots` and `og:image` items of `app.head.meta` and the `rel: 'icon'` item of `app.head.link` in `nuxt.config`, with the favicon and the social image copied to `public/`. Each edit is one value, or a new property or item written like its neighbours. A value the config computes shows as code and isn't written. Tried with Nuxt 4.

## Styles

With Tailwind 4 (a stylesheet with `@import "tailwindcss"` or an `@theme` block), the panel writes your own utilities: see [Tailwind CSS](https://midcode.app/docs/styling/tailwind.md).

Without it, midcode writes its own prefixed utilities (`mid:p-6`, `mid:md:flex`) and keeps their plain CSS in `midcode.css`, next to your `app.vue` (`app/app.vue`, or `app.vue` at the root). The first style edit creates the file and imports it from that component, as one undoable step:

```diff title="app/app.vue"
+<script setup>
+  import './midcode.css'
+</script>
+
 <template>
   <NuxtLayout>
     <NuxtPage />
   </NuxtLayout>
 </template>
```

When `app.vue` already has a `<script>`, the import goes on its first line. [Without Tailwind](https://midcode.app/docs/styling/without-tailwind.md) explains what's in the file and why every rule is `!important`.

## Limits

- Tried with Nuxt 4, component mode included. Nuxt 3 wasn't tried, and Nuxt 2 isn't covered.
- The marks come from a Vite plugin: a Nuxt app set to build with webpack gets none.
- A project without `app.vue` has nowhere midcode knows to import `midcode.css`. It creates the file and tells you ("midcode added midcode.css but couldn't tell where to import it. Import it in your app's entry so its styles show."). Import it yourself, for example by listing it under `css` in your `nuxt.config`.
- A `<template lang="pug">` isn't read.
- Elements move within one file. midcode doesn't follow what a `v-for` gives its contents, so an element that reads the loop's item can be dragged out of the loop. `⌘Z` puts it back.
- A component dragged in from the Assets tab is written without an import when its file is under `components/`, which Nuxt brings in by itself. One from another folder gets its import in the file's `<script setup>`, and is refused in a component written with the Options API. Insert's interactive components are React ("Needs a React page"), and icons go in as inline SVG.
- Markdown rendered by a content module has no marks on the canvas. A folder of `.md` files with front matter shows up in the [CMS](https://midcode.app/docs/data/cms.md) instead.
- [variables](https://midcode.app/docs/editor/variables.md) are written as in any Vue component. Variants and variables aren't written for a component on the Options API or one that lists its props as `defineProps([...])`.
- [The free canvas](https://midcode.app/docs/editor/free-canvas.md) is for Next.js projects on the App Router.

## Troubleshooting

**The site shows, but nothing can be edited.** midcode fell back to your `dev` script. Open the **Dev server log** in the top bar: it has the line that says why midcode's own start didn't come up. A config that needs something only your script sets up (an env file, a second process, a fixed port) is the usual reason.

**"Dependencies are missing".** The project has no `node_modules` yet. The canvas offers to install them with your package manager; see [Open a project](https://midcode.app/docs/start/open-a-project.md).
