Create React App, Vue CLI, Gatsby, Docusaurus
Next release
How midcode edits sites built with webpack or Rspack, which take no plugin from outside, by riding along in their own dev script.
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/clior@rspack/core;something that draws its pages:
react,preact,vueorsolid-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:
NODE_OPTIONS='--require "/Applications/midcode.app/Contents/Resources/injected/webpack-hook.cjs"' \
PORT=4310 BROWSER=none \
npm run startPORT 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:
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,.jsand.mjsfiles, with the loader that marks JSX..jsis included because that’s where Create React App writes JSX.one for
.vuefiles, 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
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:
<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 and Vite projects, and the same in .vue files as in Vue with Vite.
What you can edit
Double-click the heading and type. The string in the file is the only thing that changes:
- <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, The style panel, Insert and Select, move and resize. For what’s editable inside a .vue file, see Vue.
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:
- 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 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.
Styles
These projects rarely have Tailwind 4, so midcode writes its own mid: utilities and compiles them to midcode.css (see Without Tailwind). 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.
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 inpublic/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.htmlat its root and no script calleddevis taken for plain HTML, and midcode serves its files itself. Name the scriptdev, or say how the site runs: see Any other stack.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/pagesincluded. 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 and Ruby on Rails.
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.
A style doesn’t show. In Gatsby and Docusaurus, check that you imported src/midcode.css yourself.
More in Troubleshooting.