# Monorepos

> How midcode opens a site inside a workspace, finds its package manager and dependencies at the repository's top, and picks the site when you open the top.

- Page: https://midcode.app/docs/frameworks/monorepos
- 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.

A site inside a monorepo is edited like the same site on its own. The project, for midcode, is the site's folder (`apps/web`), not the repository. What changes is where midcode looks for the things a workspace keeps at the top: the lockfile, and often `node_modules`.

You can open the site's folder or the top of the repository. Opened at the top, midcode goes into the one site it finds there. It starts that site only: an API or another app in the same repository is yours to run.

## At a glance

| | Monorepos |
| --- | --- |
| Detected by | `pnpm-workspace.yaml`, or `workspaces` in a `package.json`, up to five folders above the site. `nx.json` and `lerna.json` count too |
| Runs with | The site's own framework, as on its page. The package manager is the one whose lockfile is at the top |
| Editing | Whatever the site's framework allows, in files inside the site's folder |
| Tried with | A pnpm workspace and an npm workspace with dependencies at the top (Vite + React, Vite + Preact) |

## Opening the site's folder

Open `apps/web` and midcode detects its framework from its own `package.json`, as usual. Three things are then answered from the repository around it when they aren't beside the site.

**Is this folder part of a workspace?** midcode walks up, at most five folders, looking for one that declares the site as one of its packages:

```yaml title="pnpm-workspace.yaml"
packages:
  - 'apps/*'
  - 'packages/*'
```

or, for npm, yarn and bun:

```json title="package.json"
{
  "workspaces": ["apps/*", "packages/*"]
}
```

The site's path has to fit one of those patterns. A repository with an `nx.json` or a `lerna.json` and no `workspaces` is taken to keep its packages in `apps/*`, `packages/*` and `libs/*`.

**Which package manager?** The site's own lockfile decides if it has one. If not, the lockfile at the top of the workspace does: `pnpm-lock.yaml`, `bun.lockb` or `bun.lock`, `yarn.lock`, `package-lock.json`. With none, npm.

**Is it installed?** Yes if there's a `node_modules` beside the site or at the top of the workspace. A `.pnp.cjs` (Yarn Plug'n'Play) counts too. So midcode doesn't offer to install because the folder next to the site is missing. When nothing is installed anywhere, "Install with pnpm" runs the install in the site's folder, with the workspace's package manager.

The dev server is then started the way the site's framework page describes. Its tools are found wherever they were installed: `next` and the framework's command the way Node resolves them, and `vite`, for the config midcode puts in front of yours, by walking up the folders until a `node_modules/vite` appears (npm hoists it to the top).

## Opening the top of the repository

Open the repository's top folder and midcode checks what's there first. If the top is itself a project it recognises, that's what opens. Otherwise it looks for the site inside:

- In a workspace, the folders its patterns name (`apps/*` means each folder of `apps/`).
- In any other repository, the folders at the top. This covers a repository that keeps its halves side by side, like `frontend/` and `backend/`, or `client/` and `server/`.

Folders that are never the site are skipped: `node_modules`, `vendor`, `dist`, `build`, `out`, `public`, `static`, `docs`, `scripts`, `test`, `tests`, `e2e`, and anything that starts with a dot.

A folder is a site when its `package.json` depends on `next`, `@react-router/dev`, `@remix-run/dev`, `astro`, `@sveltejs/kit`, `nuxt`, `vite`, `@angular/core`, `gatsby` or `@docusaurus/core`. A package with only `vite` also has to look like an app (an `index.html`, or a `src/main.tsx`, `.ts` or `.jsx`): a library built with Vite has no page to show.

Then:

- One site: midcode opens it.
- Several: it opens the one whose folder is called `web`, `www`, `site`, `website`, `app`, `frontend`, `front`, `client`, `ui`, `marketing`, `landing` or `storefront`, if exactly one is.
- Still several, or none: midcode doesn't guess. The top stays the project. If it has a `dev` script, midcode runs that and the site shows without marks, to look at and comment on. Without one, the canvas asks how the site runs. Open the site's folder instead.

When it went inside, the dev server log says so:

```text
acme holds more than a site: midcode opened apps/web and starts only that. If the site needs the rest (an API, a backend), run it yourself.
```

The project keeps the repository's name, not `web`: a generic folder name (`web`, `app`, `site`, `frontend`, `client`…) is replaced by the folder around it, and `apps` or `packages` are skipped on the way up.

## What midcode writes, and where

Everything midcode adds goes in the site's folder: `.midcode/`, and `midcode.css` when the site has no Tailwind 4. The file and line each element is marked with are relative to that folder too (`src/App.tsx:5:5`). See [What midcode adds to your project](https://midcode.app/docs/start/project-files.md).

Publish works on the repository the site is in, but lists and commits only the files under the site's folder. See [Publish](https://midcode.app/docs/publish/publish.md).

## Limits

- midcode writes only inside the folder it opened. A component that lives in another package of the workspace (`packages/ui`) is outside it: an edit to one of its elements is refused with "Outside the project". Edit it in code.
- Only the site is started. A script at the top that runs everything (`turbo dev`) isn't used, so whatever the site calls has to be running already.
- If the top-level `package.json` lists a framework itself (`vite` for tests, say), midcode takes the top as the project and doesn't look inside. Open the site's folder.
- A `.midcode/server.json` at the top also keeps midcode there: see [Any other stack](https://midcode.app/docs/frameworks/custom-server.md).
- When midcode looks for the site from the top, patterns are read one level deep: `apps/*` and `apps/**` both mean the folders of `apps/`.
- Turborepo, Nx and yarn workspaces have never been tried, and there's no record for bun or Lerna. What was tried is pnpm and npm, with Vite sites. Yarn Plug'n'Play counts as installed, but starting a dev server without a `node_modules` has not been tried.

## Troubleshooting

**midcode opened the wrong app, or the whole repository without marks.** It couldn't tell which folder is the site. Open that folder directly (`⌘O`, then `apps/web`).

**"Dependencies are missing" in a repository that is installed.** midcode didn't find the workspace: the site's folder doesn't fit a pattern in `pnpm-workspace.yaml` or `workspaces`, or the top is more than five folders up. Installing from the card still works.

**The site starts but its data doesn't load.** The API it calls isn't running. Start it yourself, in a terminal or in midcode's own (see [Code view](https://midcode.app/docs/editor/code.md)).

The framework pages cover the rest: [Next.js](https://midcode.app/docs/frameworks/nextjs.md), [React with Vite](https://midcode.app/docs/frameworks/react-vite.md), [React Router and Remix](https://midcode.app/docs/frameworks/react-router.md), [Svelte and SvelteKit](https://midcode.app/docs/frameworks/svelte.md).
