The free canvas
In Next.js App Router projects, anything you draw, drop or paste outside the breakpoints floats on the canvas. How to use it, and the page it lives in.
In a Next.js project that uses the App Router, the canvas around the breakpoints is a place to work. Anything you draw, drop or paste there floats, the way a frame does in Framer. Build a section before it goes into a page, park something you took out, or keep alternatives side by side.
What floats is real code, in one page of your project that exists only in development and that midcode never publishes.
Where it works
In Next.js projects that use the App Router (an app/ or src/app/ folder). From the next release, also in Next.js projects on the Pages Router and in Vite apps written in React, Preact or Solid (Other React projects), and, for plain elements, in Vue, Svelte, Astro and HTML projects. In every other project the canvas outside the breakpoints is empty space: F inserts a frame into the selection instead, and dropping there puts the element at the end of the page. If you drag an element out of a frame anyway, midcode says “Things float on the canvas in Next.js App Router projects, for now”.
Other React projectsNext release
In the next release the free canvas works the same in two more kinds of project. Only the file that holds what floats is somewhere else:
| Project | The file | Why it’s never published |
|---|---|---|
| Next.js, App Router | app/midcode-scratch/page.tsx | It answers 404 in production |
| Next.js, Pages Router | pages/midcode-scratch.tsx | Its getStaticProps answers “not found” in production |
| Vite + React, Preact or Solid | src/midcode-scratch.tsx | Nothing in your app imports it, so it’s never built |
In a Vite app there is no route for that file. While midcode runs your dev server, its plugin answers /midcode-scratch with your own index.html, with the app’s entry replaced by one that draws that component and loads the stylesheets your entry imports. midcode leaves the file out of Publish in all three.
A Vite app needs an index.html at its root for this: TanStack Start, Qwik and a Laravel app with Vite don’t have the free canvas.
Vue, Svelte, Astro and HTMLNext release
Outside React, what floats is plain HTML, the same in every one of these: a file with only the canvas’s <main> in it.
| Project | The file |
|---|---|
| Vite + Vue, Vite + Svelte, SvelteKit, Astro | src/midcode-scratch.html |
| A folder of HTML | midcode-scratch.html, beside your index.html |
It isn’t a page of the site. While midcode runs the site it serves a page around that file, at /midcode-scratch.html, with your stylesheets: the ones your entry, App.vue, the root +layout.svelte or your Astro layouts import, or in a folder of HTML the <head> of your home page. midcode leaves the file out of Publish.
Because the file is HTML and not a .vue, .svelte or .astro file, only plain elements can float:
A component can’t: “A component can’t float on the canvas in this kind of project: only plain elements do.”
An element with something computed in it can’t either (
{{ title }}, av-for, an{#if}, a bound attribute): “This has code in it ({{ }}, a loop, a condition): only plain elements float on the canvas in this kind of project.”
Everything else works as in React: draw a frame, drop something from Insert, drag an element out of a page and back in, copy with ⌥, wrap several in a stack, move them with the arrows. Going into a .svelte or .astro page, an element whose text has a { or a } is refused, since the page would read it as code.
Nuxt and the templates a server renders (Laravel, Django, Rails, WordPress, Shopify themes) don’t have the free canvas.
Put something on the canvas
| To | Do this |
|---|---|
| Draw a frame | Press F (or the frame button in the toolbar) and drag on the canvas. A label shows the size. A click without dragging makes one of 200 × 200. Esc puts the tool away. |
| Take an element out of a page | Drag it out of its frame and release over the empty canvas. |
| Copy an element out of a page | The same, holding ⌥: the element stays and a copy floats. |
| Add something new | Drag an item from the Insert panel, or a component from Assets, and release over the empty canvas. |
| Add an image or a video | Drop files from Finder on the empty canvas, or press ⌘V with an image on the clipboard and nothing selected. See Text, images and video. |
| Paste an element | ⌘C on any element, deselect (Esc), ⌘V. The copy lands in the middle of what’s in view. |
A floating element is designed at the primary breakpoint’s width: its responsive classes show the values of that breakpoint.
An element with no width of its own is as wide as its content. A text or a stack that has nothing behind it is shown over your page’s background color, so light text chosen for a dark page stays readable. Frames and images float as they are.
Select and arrange
Click a floating element to select it. It works like any other selection: the right panel edits it, a double-click edits its text, and its own layers are in Layers, after the breakpoints.
| To | Do this |
|---|---|
| Move | Drag it. A label shows its position. |
| Move by 1 px | Arrow keys. With ⇧, by the nudge amount set in Settings. |
| Resize | Drag any of its eight handles. ⇧ on a corner keeps the proportions. |
| Rotate | Drag just outside a corner. ⇧ turns in steps of 15°. |
| Select several | Drag a box over them on the empty canvas, or ⇧ + click each one. ⌘A with nothing selected takes all of them. |
| Duplicate | ⌘D, or hold ⌥ while dragging. |
| Delete | ⌫ |
| Wrap in a stack | Select several and press ⇧A. |
Several selected elements move together, with the pointer or the arrow keys, and ⌘C, ⌘X, ⌘D and ⌫ apply to all of them.
A copy never lands on top of its original. ⌘D and ⌘V put it to the right of what’s selected, 40 px away. Copies of several keep their places among themselves.
⇧A on floating elements makes one floating stack in their place. It’s a row if they sit side by side and a column if they sit one above the other, in that order, and the space between them becomes its gap.
Snapping
While you move or resize a floating element, it lines up with the edges and centers of the other floating elements, with the breakpoints’ frames, and with guides. It also snaps to the same distance its neighbours keep. Lines and distances show what it found.
Hold ⌘ or ⌃ to move freely. ⇧ keeps the move on one axis. Rulers and guides are covered in Select, move and resize.
Into a page and back
Drag a floating element over a breakpoint. The blue line shows where it would land in the page, as when you move an element. Release, and it’s written into the page’s file, at that place, without its canvas marks. The imports it needs go with it.
Drag it over another floating element instead and it goes inside that one.
Going the other way, an element dragged out of a page is cut from its file and written into the canvas page, with its imports. Either direction is one edit: one ⌘Z puts the element back where it was.
What can’t cross is an element that depends on where it’s written, like an item inside a .map() that reads the loop’s values: “This element uses post from its file: move it in the code.”
What midcode writes
Everything that floats lives in one file: app/midcode-scratch/page.tsx (under src/app/ if that’s where your app is, and .jsx if the project has no tsconfig.json). midcode creates it the first time something floats:
import { notFound } from 'next/navigation'
// What floats on midcode's canvas, outside the site's breakpoints. It only exists in development
// (a 404 on the live site), and midcode never publishes it.
export default function MidcodeCanvas() {
if (process.env.NODE_ENV === 'production') notFound()
return (
<main data-midcode-scratch>
</main>
)
}Each floating element is a direct child of that <main>, with two attributes: an id, and where it sits.
import { notFound } from 'next/navigation'
import { PricingCard } from '@/components/PricingCard'
// What floats on midcode's canvas, outside the site's breakpoints. It only exists in development
// (a 404 on the live site), and midcode never publishes it.
export default function MidcodeCanvas() {
if (process.env.NODE_ENV === 'production') notFound()
return (
<main data-midcode-scratch>
<div data-midcode-item="3f9a1c2e" data-midcode-at="2960 80" className="h-[320px] w-[480px] bg-white" />
<h2 data-midcode-item="b71d04aa" data-midcode-at="2960 480" className="text-4xl font-semibold">
Simple pricing
</h2>
<div data-midcode-item="0c5e77d1" data-midcode-at="3520 80" data-midcode-wrap>
<PricingCard plan="Studio" />
</div>
</main>
)
}| Attribute | What it is |
|---|---|
data-midcode-scratch | Marks the <main> that holds everything floating. |
data-midcode-item | The element’s id on the canvas: eight characters, unique in the file. |
data-midcode-at | Its position, x y, in canvas px. 0 0 is the top left corner of the first breakpoint’s frame. The rulers show the same coordinates. |
data-midcode-wrap | On the <div> around a component. A component might not pass unknown attributes on to its element, so the marks go on a wrapper. |
The first element above is a frame drawn with F: a <div> with its size and a white fill.
Every gesture is an edit of that file:
Moving changes
data-midcode-at. That’s why ⌘Z moves an element back.Resizing writes
w-*andh-*. From the left or the top it changes the position too.Rotating writes a class:
rotate-[12deg]. Back at 0°, the class is removed.Going into a page removes the two attributes (and the wrapper around a component) and moves the code to the page’s file.
Never published
The page calls
notFound()whenNODE_ENVisproduction. On your live site,/midcode-scratchanswers 404.midcode leaves it out of Publish: it’s never in the list of files, and its edits aren’t in the list of changes.
It isn’t in the Pages list or in the texts of Languages.
Editing the file by hand
You or your agent can write in it. The rules are the ones above: everything directly inside <main data-midcode-scratch> needs its own data-midcode-item and a data-midcode-at. When midcode reads a file that breaks them, it repairs it:
two elements with the same id: the second gets a new one;
an element with no marks: it gets them, where what’s inside it was, or below everything else;
marks on an element nested inside a floating one: removed.
Delete the file (or the midcode-scratch folder) to clear the canvas. midcode makes a new one the next time something floats.
Limits
In the released version, Next.js App Router only. The next release adds the Pages Router, Vite apps in React, Preact or Solid, and, for plain elements, Vue, Svelte, Astro and folders of HTML. Nuxt and server templates don’t have it yet.
A floating element shows the primary breakpoint’s styles. To see something at other widths, put it in a page.
A component floats inside a wrapper
<div>that is as wide as its content, unless you give the wrapper a width.Dragging into another floating element works for one element at a time, not for several selected together.
The canvas page is a page of your app: it renders inside your root layout, so the layout’s providers, fonts and global styles apply to what floats.