# Open a project

> How midcode opens a project folder, what it detects, how it starts your dev server, where the log is, and why a project can be view-only.

- Page: https://midcode.app/docs/start/open-a-project
- From the midcode docs. Every page as Markdown: https://midcode.app/llms.txt

midcode opens a folder. It works out what runs the site in it, starts that dev server on a port of its own, and shows the running site at every breakpoint, side by side. Nothing is imported or converted: the canvas is your dev server.

## Open a folder

Any of these opens a project:

- Press `⌘O` (File → "Open Folder…") and choose the folder.
- Click "Open folder" on the hub, or the + at the end of the tab strip. From the next release the + starts a [new project](https://midcode.app/docs/start/new-project.md) instead: it goes to the hub with "New project" open, and "Open an existing folder…" in that dialog opens a folder.
- Drop the folder on the hub.

Choose the project's root: the folder with its `package.json`, or with its `index.html` when it's plain HTML.

The hub lists the projects you've opened, each with a picture of the site and when you last opened it. The ⋯ on a card has "Open", "Open in Finder", "Open in code editor", "Copy path" and "Remove from recents". Removing one only takes it off the list. From the next release it reads "Remove from the list", and the hub can be arranged your way: [Organize the hub](#organize-the-hub).

### Tabs

Each project opens in its own tab, like a browser. Every tab has its own dev server, and it keeps running while another tab is in front. `⇧⌘H` (File → "Back to Projects") goes to the hub without closing anything. Closing a tab stops that project's dev server, its terminals and its agent.

A project opens once: opening it again brings its tab forward.

## Organize the hub (next release)

The hub keeps every project you've opened, and you arrange it your way. The arrangement is midcode's own list: no folder is moved, renamed or changed on your disk.

- Groups. The + beside "Groups" in the sidebar makes a group and lets you type its name. Drag a project's card onto a group to put it there, or use "Move to group…" in the card's ⋯ menu (a right-click on the card opens the same menu). A project is in one group at a time. "Ungrouped" shows the ones in none, and a card dropped on it leaves its group.
- Pins. The pin on a card, or "Pin to the top" in its menu, keeps the project first, under "Pinned", in every view.
- Order. The menu at the top right orders the cards by "Last opened", by "Name" or in a "Custom order". Drag a card beside another one to set your own: the order becomes "Custom order", starting from what was on screen, so only the card you dragged moves. Pinned cards are ordered among themselves.
- Search. "Search projects" filters the open view by name or by path.

A group's row has its own ⋯ (and right-click) with "Rename" and "Delete group". Double-click a group to rename it, and drag it to reorder the groups. Deleting a group keeps its projects on the hub, in no group.

Dragging a card out of midcode, onto a terminal or an editor, drops the project's path.

All of it is one file in midcode's own data folder, never in a project:

```json title="~/Library/Application Support/midcode/hub.json"
{
  "groups": [{ "id": "531a1d5e", "name": "Clients" }],
  "where": { "05393af05cce": "531a1d5e" },
  "pinned": ["5968533421fe"],
  "order": ["05393af05cce", "74cd0d0aa0b1"],
  "sort": "manual"
}
```

Projects are known there by an id made from their path. "Remove from the list" also takes a project out of its group, its pin and its place in the order. A project whose folder is missing (an unplugged disk) isn't listed, and finds its group again when the folder is back.

## What midcode detects

midcode reads the dependencies in `package.json`. The first one it finds, in this order, decides how the project runs:

| In `package.json` | Opens as | On the canvas |
| --- | --- | --- |
| `next` | [Next.js](https://midcode.app/docs/frameworks/nextjs.md) | Edited. |
| `@react-router/dev` | [React Router](https://midcode.app/docs/frameworks/react-router.md) | Edited. |
| `@remix-run/dev` | [Remix](https://midcode.app/docs/frameworks/react-router.md) | Edited. |
| `astro` | [Astro](https://midcode.app/docs/frameworks/astro.md) | Its React, Preact and Solid islands are edited. `.astro` files: next release. |
| `@sveltejs/kit` | [SvelteKit](https://midcode.app/docs/frameworks/svelte.md) | Edited. |
| `nuxt` | [Nuxt](https://midcode.app/docs/frameworks/nuxt.md) | View-only. Edited in the next release. |
| `vite` | [Vite](https://midcode.app/docs/frameworks/react-vite.md) | Edited with `react`, `preact`, `solid-js` or `svelte` in the dependencies. With `vue` or Qwik: next release. Otherwise view-only. |
| none of these, and a `dev` script | Your `dev` script | View-only. |
| no `package.json` (or none of the above in it), and an `index.html` | [Plain HTML](https://midcode.app/docs/frameworks/html.md) | View-only. Edited in the next release. |

[What works with what](https://midcode.app/docs/start/supported-stacks.md) has the whole list, with how much of each stack midcode edits.

### More stacks, and any folder (next release)

In the released version, a folder that matches no row of the table above (no known framework, no `dev` script and no `index.html`) is refused: "midcode couldn't tell what runs this folder." From the next release, any folder opens:

- Stacks midcode knows by their files run with their own command: Laravel, Django, Flask, FastAPI, Rails, Hugo, Jekyll, WordPress, plain PHP, Angular, a Shopify theme and more. The list and each command are in [Any other stack](https://midcode.app/docs/frameworks/custom-server.md).
- Sites built with webpack or Rspack (Rsbuild among them) run with their own script, on midcode's port where the tool takes `--port`, and are edited: [Create React App, Vue CLI, Gatsby, Docusaurus](https://midcode.app/docs/frameworks/webpack.md).
- Sites a server renders from templates are edited too, in a different way: [Sites a server renders](https://midcode.app/docs/frameworks/server-templates.md).
- The top of a repository with one site inside (`apps/web`, `frontend/`) opens that site. See [Monorepos](https://midcode.app/docs/frameworks/monorepos.md).
- When midcode can't tell how a folder runs, the canvas asks. See [Change how it runs](#change-how-it-runs).

## Dependencies

If the project has a `package.json` and no `node_modules`, the canvas shows "Dependencies are missing" with one button: "Install with pnpm" (or npm, yarn, bun). The package manager is the one your lockfile names: `pnpm-lock.yaml`, `bun.lockb` or `bun.lock`, `yarn.lock`, `package-lock.json`. With no lockfile it's npm.

The button runs `<package manager> install` in your login shell, in the project folder, and then starts the server. What it prints goes to the log.

## How the dev server starts

midcode picks a free port, starting at 4310, so it never takes the port your own terminal uses. It runs the server with the Node your terminal has (it asks your login shell for its `PATH`, so nvm, fnm and Homebrew setups are found), or with its own Node when the Mac has none.

| Project | What midcode runs |
| --- | --- |
| Next.js | `next dev --port <port>`, with midcode's hook preloaded |
| Vite, SvelteKit | `vite dev --port <port>`, with a config that loads yours and adds midcode's plugin |
| React Router | `react-router dev --port <port>`, the same way |
| Remix | `remix vite:dev --port <port>`, the same way |
| Astro | `astro dev --port <port>`, the same way |
| Nuxt | `nuxi dev --port <port>`. Next release: with a layer that adds midcode's plugin |
| Plain HTML | midcode's own static server, which reloads the page when a file changes |
| Anything else | `<package manager> run dev`, and the address is read from what it prints |

The hook and the plugin are what tell the canvas where each element is written. They're added from outside, on the command line: your `next.config`, `vite.config` and `package.json` are not changed. [How midcode works](https://midcode.app/docs/start/how-it-works.md) follows that road end to end.

From the next release, a Vite project is also started with the options its own `dev` script gives Vite (`vite --mode ssr` keeps `--mode ssr`). The port, the host and the config stay midcode's.

For that process midcode also sets `PORT`, sets `BROWSER=none` so no browser window opens, and turns off Next.js and Astro telemetry.

The server is ready when the site answers. The first line of the log says what was run and with which Node:

```text
$ next dev --port 4310  (/Users/you/.nvm/versions/node/v22.11.0/bin/node)
```

### When midcode's way doesn't start

Some projects need something only their own script sets up: an env file, a second process, a toolchain that wraps Vite. For Vite, SvelteKit, React Router, Remix, Astro and Nuxt projects, if the server stops or doesn't answer in 45 seconds, midcode stops it and runs your own `dev` script instead, with 2 minutes to come up.

The site then shows, but without midcode's plugin there's nothing to edit by, so the project is view-only for that session. A message says so: "midcode couldn't start this project its own way, so it runs your dev script: the site shows, but editing on the canvas needs midcode's plugin. The log says why." Its "Send report" button opens the feedback form with the report attached.

Next.js has no fallback. It always runs with the hook.

## The server log

The button at the right of the top bar, a dot beside a terminal icon, opens the dev server's log. The dot is green while the server runs. While it starts, installs or fails, a word sits beside it: "Starting", "Installing", "Error", "Stopped".

The log panel has "Send report", "Copy report", "Restart" and a close button. An error opens it by itself. [Troubleshooting](https://midcode.app/docs/reference/troubleshooting.md) says what a report contains.

## One Next.js dev server per folder

Next.js allows one `next dev` per project folder. If yours is already running in a terminal, midcode's stops and the canvas shows "A Next server is already running for this project", with the address and process id of the other one.

- "Stop it and use midcode" stops that process (midcode checks first that it is a Next.js server) and starts its own.
- Or stop it yourself in your terminal, then click "Restart" in the log.

midcode needs its own server: the one in your terminal runs without the hook, so its pages don't say where each element is written.

## HTTPS and other hosts

- A dev server that serves `https` with a certificate it made for itself (Vite's basic-ssl plugin, mkcert) works. midcode reads the address from the server's "Local:" line, `http` or `https`, and accepts a self-made certificate for `localhost`, `127.0.0.1` and `*.localhost` only.
- The released version only shows a site at `localhost` or `127.0.0.1`. From the next release a site can live on another host (`app.test` under Herd or Valet), which needs a certificate macOS trusts.
- A Laravel app with Vite is shown at the `APP_URL` of its `.env`, as served by Herd, Valet or `php artisan serve`, with Vite running beside it. If `APP_URL` is a plain `http` address on this Mac and nothing answers there, midcode starts `php artisan serve` itself. In the released version a Laravel site is shown only when that address is `localhost` or `127.0.0.1`, and it's view-only: see [Laravel](https://midcode.app/docs/frameworks/laravel.md).

## Change how it runs (next release)

When a server fails, the error card has a "Change how it runs" link. It opens the same form the canvas shows for a folder it can't work out: "How does this site run?"

- "Command": what starts the site, for example `bin/rails server -p $PORT`. `$PORT` is the free port midcode picked.
- "Address": where it answers, for example `http://localhost:$PORT`.

Give one or both. With an address and no command, midcode starts nothing and only shows a site that's already running (under Herd, Docker, another terminal). "Start" saves it in the project:

```json title=".midcode/server.json"
{
  "command": "bin/rails server -b 127.0.0.1 -p $PORT",
  "url": "http://127.0.0.1:$PORT"
}
```

What's in that file comes before anything midcode would do by itself. "Reset" empties it and lets midcode work it out again.

> [!NOTE]
> In a project midcode already knows how to start (Next.js, Vite, Astro, Nuxt, SvelteKit, React Router, Remix), a `server.json` makes it run the project's own way, without midcode's hook or plugin: the site shows, with no marks, and it's view-only. To get midcode's own start back, delete the file and click "Restart" in the log.

[Any other stack](https://midcode.app/docs/frameworks/custom-server.md) has the details.

## Editable or view-only

A project is editable when midcode has a way to know where each element is written. When it doesn't, the right panel says so, for example "Nuxt: preview and comments", and you can still:

- see every breakpoint, and reload them;
- use the site at full size with [Preview](https://midcode.app/docs/editor/canvas.md) (`P`);
- leave [comments](https://midcode.app/docs/editor/comments.md) for your agent, each anchored to its element;
- browse the page in [Layers](https://midcode.app/docs/editor/layers.md), by its DOM;
- work on the code in [Code](https://midcode.app/docs/editor/code.md) and with [your agent](https://midcode.app/docs/agents/overview.md);
- [publish](https://midcode.app/docs/publish/publish.md).

A project is view-only when its stack isn't one midcode edits yet (the table above), when it's running with its own `dev` script because midcode's way didn't start, or, from the next release, when a `.midcode/server.json` tells a Next.js, Vite, Astro, Nuxt, SvelteKit, React Router or Remix project to run its own way.

## Limits

- midcode starts the site and nothing else. If your site needs an API or a database running beside it, start those yourself.
- With no Node on the Mac, midcode runs your dev server with its own, but it can't install dependencies: that needs your package manager.
- midcode picks the port for the servers it starts itself. A `dev` script with a fixed port still fails if that port is taken: what it prints is in the log. From the next release, a script that is only a call to a tool that takes `--port` and doesn't name one (rsbuild, rspack, webpack, vue-cli-service, docusaurus, gatsby, parcel, vitepress, vuepress, eleventy) is given midcode's port.
- The routes midcode lists as pages come from each framework's files. How they're found is on each framework's page, and in [Pages and navigation](https://midcode.app/docs/editor/pages.md).
