Skip to content

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.

View as Markdown

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 byA tool that runs webpack or Rspack, together with react, preact, vue or solid-js, in package.json
Runs withYour 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 byTwo loaders the hook puts first in the config webpack or Rspack is given
EditingText, classes, attributes and structure, in the project’s .js, .jsx, .tsx and .vue files
ComponentsInstances and props are recognised, as in any JSX or Vue project
PagesGatsby: the files of src/pages. The others: none listed
Stylesmid: classes, with midcode.css imported in src/index.js or src/main.js
Tried withCreate 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:

Terminal
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

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 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:

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, 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:

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 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.

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.

StackMarkedNotes
Create React App 513 of 15The other two are written in public/index.html. Text, classes and mid: styles show through hot reload
Vue CLI 536 of 39Text, classes and mid: styles show through hot reload
Gatsby 535 of 41All of them from src/pages/index.js. Its pages are listed
Docusaurus 321The 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.

  • 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 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.