# Create React App, Vue CLI, Gatsby, Docusaurus

> How midcode edits sites built with webpack or Rspack, which take no plugin from outside, by riding along in their own dev script.

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

Sites built with webpack or Rspack are edited on the canvas too: Create React App, Vue CLI, Gatsby, Docusaurus, Rsbuild, or a webpack config you wrote yourself. None of these tools takes a plugin from outside the way Vite and Next.js do, so midcode goes in another way. It runs the project's own script and preloads a hook into it. The hook adds midcode's loaders to whatever config webpack is called with.

What you can edit is the code you wrote: the JSX and the Vue components in the project's own folders. What a theme or a generated folder draws has no marks.

## At a glance

| | webpack and Rspack sites |
| --- | --- |
| Detected by | A tool that runs webpack or Rspack, together with `react`, `preact`, `vue` or `solid-js`, in `package.json` |
| Runs with | Your own script (`dev`, `start`, `serve`, `develop` or `docs:dev`), in your shell, on a port midcode picks, with one `--require` added through `NODE_OPTIONS` |
| Elements are marked by | Two loaders the hook puts first in the config webpack or Rspack is given |
| Editing | Text, classes, attributes and structure, in the project's `.js`, `.jsx`, `.tsx` and `.vue` files |
| Components | Instances and props are recognised, as in any JSX or Vue project |
| Pages | Gatsby: the files of `src/pages`. The others: none listed |
| Styles | `mid:` classes, with `midcode.css` imported in `src/index.js` or `src/main.js` |
| Tried with | Create React App 5, Vue CLI 5, Gatsby 5, Docusaurus 3, Rsbuild 2 with React, a webpack 5 config written by hand |

## How midcode runs it

midcode reads `package.json`. The project is one of these when it has no `next`, `vite`, `astro`, `@sveltejs/kit`, `nuxt`, `@react-router/dev` or `@remix-run/dev` (those have their own pages), and its dependencies hold both:

- something that runs webpack or Rspack: `react-scripts`, `@craco/craco`, `react-app-rewired`, `@vue/cli-service`, `gatsby`, `@docusaurus/core`, `webpack-dev-server`, `webpack-cli`, `@webpack-cli/serve`, `@rsbuild/core`, `@rspack/cli` or `@rspack/core`;
- something that draws its pages: `react`, `preact`, `vue` or `solid-js`.

The script it runs is the first one your `package.json` has among `dev`, `start`, `serve`, `develop` and `docs:dev`. That's `start` in Create React App and Docusaurus, `serve` in Vue CLI, `start` or `develop` in Gatsby. It runs in your login shell, with your package manager and your Node, as if you had typed:

```bash
NODE_OPTIONS='--require "/Applications/midcode.app/Contents/Resources/injected/webpack-hook.cjs"' \
PORT=4310 BROWSER=none \
npm run start
```

`PORT` is a free port, the first from 4310 up, for the tools that honour it (Create React App does). `BROWSER=none` keeps the script from opening a browser tab. midcode also passes the hook the project's folder and where its loaders are, in variables named `MIDCODE_…`. The address the site answers at is read from what the script prints.

Some of these tools don't read `PORT`. Left alone they'd take their usual port (3000, 8000, 8080), which may belong to a server of your own. So when your script is only a call to one of them and names no port, midcode adds `--port` with its own: `npm run serve -- --port 4310` in a Vue CLI project (`pnpm run serve --port 4310` with pnpm, yarn or bun). The tools it does this for are `rsbuild`, `rspack`, `vue-cli-service`, `webpack`, `webpack-dev-server`, `docusaurus`, `gatsby`, `parcel`, `vitepress`, `vuepress` and `eleventy`. A script that does more than call the tool (`a && webpack serve`), or that already has `--port` or `-p`, runs as it is.

The dev server log (the button with the status dot in the top bar) says so:

```text
Built with webpack: midcode adds its stamps as the site builds.
$ npm run start  (your shell)
```

The hook is loaded into every Node process the script starts. It does nothing until one of them requires `webpack` or `@rspack/core`. Rspack 2 is an ES module, which Rsbuild 2 imports instead of requiring: there the hook answers the import with a module that hands out the same wrapped `rspack()`. That part needs a Node that can hook into imports (22.15 or later). Then, each time that tool is called with a config, the config gains two rules, ahead of its own:

- one for `.jsx`, `.tsx`, `.js` and `.mjs` files, with the loader that marks JSX. `.js` is included because that's where Create React App writes JSX.
- one for `.vue` files, with the loader that marks Vue templates.

Both skip anything outside the project folder, anything in `node_modules`, and any folder whose name starts with a dot (Gatsby's `.cache`, Docusaurus's `.docusaurus`). A config that builds for Node (`target: 'node'`, a server-side build) is left as it is. Nothing is written to disk, and nothing in your webpack config, `craco.config.js`, `vue.config.js`, `gatsby-config.js` or `docusaurus.config.js` changes.

### What the loaders add

```jsx title="src/App.js"
function App() {
  return (
    <div className="App">
      <h1>Hello</h1>
    </div>
  );
}

export default App;
```

In the page, the heading carries the file, line and column it's written at:

```html
<h1 data-mc="src/App.js:4:7">Hello</h1>
```

Component instances get `data-mci` as a prop (`App|src/index.js:10:5`), and the element a component returns carries where it's used in `data-mcu`. They are the same three marks midcode leaves in [Next.js](https://midcode.app/docs/frameworks/nextjs.md) and Vite projects, and the same in `.vue` files as in [Vue](https://midcode.app/docs/frameworks/vue.md) with Vite.

## What you can edit

Double-click the heading and type. The string in the file is the only thing that changes:

```diff title="src/App.js"
-      <h1>Hello</h1>
+      <h1>Hello again</h1>
```

Text, classes, attributes, images, inserting, moving, duplicating and removing work as in any JSX or Vue project: see [Text, images and video](https://midcode.app/docs/editor/text-and-media.md), [The style panel](https://midcode.app/docs/editor/styles.md), [Insert](https://midcode.app/docs/editor/insert.md) and [Select, move and resize](https://midcode.app/docs/editor/select-move-resize.md). For what's editable inside a `.vue` file, see [Vue](https://midcode.app/docs/frameworks/vue.md).

A text that isn't in the markup is found where it's written. In Docusaurus the tagline on the home page comes from the config, and that's where the edit lands:

```diff title="docusaurus.config.js"
-  tagline: 'Dinosaurs are cool',
+  tagline: 'Docs that stay current',
```

## Pages

In Gatsby, each file of `src/pages` (`.js`, `.jsx`, `.tsx`, `.mdx`) is a page, except `404` and `500`. The other tools have no list midcode reads: the page menu shows Home. To see another page, type its path in the menu and press `Enter`.

## Site settings

In Create React App and Vue CLI the site's `<head>` is a file, `public/index.html`, and [Site settings](https://midcode.app/docs/editor/site-settings.md) writes its tags there: the title, the description, the language, search engines, the favicon and the social image. Images are copied into `public/`.

What the bundler fills in when it builds is shown as code and left alone. Vue CLI's `<title><%= htmlWebpackPlugin.options.title %></title>` is one of those: the form shows it, and the title stays your code's to set. Tried with Vue CLI.

Gatsby keeps these settings in its own config, and midcode doesn't write them yet.

Docusaurus keeps these settings in `docusaurus.config`, and Site settings reads and writes them there: `title`, `tagline` (the description), `i18n.defaultLocale`, `noIndex`, `favicon` and `themeConfig.image`, with images copied to `static/img`. Tried with Docusaurus 3. See [Nuxt and Docusaurus](https://midcode.app/docs/editor/site-settings.md).

## Styles

These projects rarely have Tailwind 4, so midcode writes its own `mid:` utilities and compiles them to `midcode.css` (see [Without Tailwind](https://midcode.app/docs/styling/without-tailwind.md)). On your first style edit it creates `src/midcode.css` and imports it in the entry it finds: `src/index.js` in Create React App, `src/main.js` in Vue CLI.

```diff title="src/index.js"
 import './index.css';
 import App from './App';
 import reportWebVitals from './reportWebVitals';
+import './midcode.css'
```

Gatsby and Docusaurus have no such entry file. There midcode still writes `src/midcode.css` and tells you it couldn't tell where to import it. Load it where the site takes its global CSS: `gatsby-browser.js` in Gatsby, `customCss` in `docusaurus.config.js`. Styles were not tried on those two.

## What was tried

Each number is the elements of one page that carried a mark, out of all the elements on it.

| Stack | Marked | Notes |
| --- | --- | --- |
| Create React App 5 | 13 of 15 | The other two are written in `public/index.html`. Text, classes and `mid:` styles show through hot reload |
| Vue CLI 5 | 36 of 39 | Text, classes and `mid:` styles show through hot reload |
| Gatsby 5 | 35 of 41 | All of them from `src/pages/index.js`. Its pages are listed |
| Docusaurus 3 | 21 | The project's own JSX in `src/pages` and `src/components`. Docusaurus 3 builds with Rspack |

Rsbuild 2 with React and a webpack 5 config written by hand (babel-loader and webpack-dev-server) were tried too, with no count on record.

## Limits

- Only your own source is marked. In Docusaurus most of a page is the theme, which comes from `node_modules`, and the docs are MDX: neither can be selected by its code. The same goes for what's written in `public/index.html`.
- Rsbuild 2 imports Rspack as an ES module, and the hook can only answer an import on Node 22.15 or later. On an older Node the site runs and nothing is marked.
- Parcel and Nuxt 2 are not covered.
- The hook travels in `NODE_OPTIONS`, so the script has to run with a Node from your shell's PATH, and neither the script nor your shell profile may set that variable to something else.
- A project with an `index.html` at its root and no script called `dev` is taken for plain HTML, and midcode serves its files itself. Name the script `dev`, or say how the site runs: see [Any other stack](https://midcode.app/docs/frameworks/custom-server.md).
- Component mode, variants and variables were not tried on these stacks. What was tried is text, classes and styles.
- midcode's own interactive components (Carousel, Tabs and the others) are React. A Vue CLI project can't take them.
- No new pages: midcode doesn't write them in these projects, Gatsby's `src/pages` included. No free canvas: it's for Next.js projects on the App Router.
- Site settings doesn't write Gatsby's config, which is where Gatsby keeps the site's title and the rest.
- A Laravel or Rails app that builds its assets with webpack is served as Laravel or Rails: see [Laravel](https://midcode.app/docs/frameworks/laravel.md) and [Ruby on Rails](https://midcode.app/docs/frameworks/rails.md).

## Troubleshooting

**The site shows but nothing can be selected by its code.** The hook didn't reach webpack. Check your script: if it sets `NODE_OPTIONS` itself (`NODE_OPTIONS=--openssl-legacy-provider react-scripts start`), that replaces midcode's. Keep what's already there instead: `NODE_OPTIONS="$NODE_OPTIONS --openssl-legacy-provider" react-scripts start`. An `export NODE_OPTIONS=…` in your shell profile does the same and needs the same change.

**The server never answers.** midcode waits two minutes for the address the script prints, or for an answer on the port it passed. A script that listens somewhere else without printing a `localhost` address can't be found: say where it runs in [Any other stack](https://midcode.app/docs/frameworks/custom-server.md).

**A style doesn't show.** In Gatsby and Docusaurus, check that you imported `src/midcode.css` yourself.

More in [Troubleshooting](https://midcode.app/docs/reference/troubleshooting.md).
