Text, images and video
Edit a text where it shows and midcode changes it where it's written, even in a data file. Replace, paste and drop images and videos, and set alt text.
Text and media are the two things on a page that are content rather than layout. midcode edits both where they show, on the canvas, and writes the change where the content lives in your project: in the markup, in a data file, or as a file in public/.
Edit a text
Double-click a text on the canvas. It becomes editable in place, with its words selected.
Type.
Press Enter, or click somewhere else, to save. Esc cancels.
midcode says where it went: “Saved to src/app/page.tsx”. Your dev server reloads the text from the file, so what you see afterward is the code.
You can also select the element and edit the “Text” field in the right panel. It writes the same way.
The text is plain: no bold, no links, no line breaks. A line break typed with ⇧Enter is saved as a space.
Which elements can be edited in place
An element whose content is only text. <h1>Design in code</h1> is. A paragraph with an element inside is not one text:
<p>
Free for 3 days, then <strong>one payment</strong>.
</p>Double-click “one payment” and you edit the <strong>. The words around it belong to the paragraph, mixed with an element, and can’t be edited in place: use Open code.
Double-clicking something that isn’t a text does something else: on a component’s instance, it opens the component.
Where the text is written
midcode looks in three places, in order, and edits the first that has the text.
In the element
The usual case: the words are between the element’s tags.
-<h1 className="text-5xl font-semibold">Design in code</h1>+<h1 className="text-5xl font-semibold">Design, in your code</h1>Only the text changes. Indentation and line breaks around it are kept. A text written as a string in braces counts too: {'Design in code'}.
If your new text has a character JSX would read as code ({, }, <, >), it’s written as a string: {"Save 20% when x > 3"}. If the text you’re replacing wrote its apostrophes as ', the new one does too.
Where the component is used
A component that renders {children} has no words of its own. They’re written where the component is used, and that’s the line midcode edits:
-<Button href="/download">Download</Button>+<Button href="/download">Download for Mac</Button>This works when the element on the page knows which use of the component drew it, which it does when the component passes its remaining props on to its element (<a {...props}>). A component that takes children and passes nothing else on leaves midcode with no way to tell which use wrote the words. It says the text comes from a prop, and you edit it in the code.
In your data
Sites built with an agent often keep their copy apart from the markup: an array of features in content/home.ts, rendered with .map(). The element says {feature.title}, and the words are somewhere else.
import { features } from '@/content/home'
export function Features() {
return (
<ul>
{features.map((feature) => (
<li key={feature.title}>
<h3>{feature.title}</h3>
<p>{feature.body}</p>
</li>
))}
</ul>
)
}When the element has no literal text, midcode searches the project for the exact text as a string and edits it there:
export const features = [- { title: 'Edits are code', body: 'Every change is written into your files.' },+ { title: 'Every edit is code', body: 'Every change is written into your files.' }, { title: 'No lock-in', body: 'Your project stays a normal repository.' }, ]The message then names the data file and the line: “Saved to src/content/home.ts:2”.
What’s searched: .ts, .tsx, .js, .jsx, .mjs, .cjs, .json, .md, .mdx and .svelte files, outside node_modules, build folders, public/ and folders whose name starts with a dot. In code and JSON the whole string has to be the text, between its quotes. In Markdown the text can be anywhere in the file. No other kind of file is read: a text kept in a YAML or TOML file, or in a .vue, .astro, .html or .php file other than the element’s own, isn’t found.
That covers arrays and objects in modules, JSON files, message catalogs like messages/en.json, and Markdown content.
If the text appears once, that’s the one. If it appears several times, midcode narrows it down: first to the files the element’s file imports, then to the exported values it imports by name. If it still can’t tell, it doesn’t guess:
That text appears in 3 places (src/content/home.ts:2, src/content/pricing.ts:8, messages/en.json:14). Open the right one in your editor.
For content kept as lists, the CMS shows the same data as a table. For a site in several languages, Languages edits each language’s text side by side.
What can’t be edited on the canvas
Text put together by code.
{count} items, a formatted date, a template string with a value in it. There’s no string in your files that equals what’s on screen. midcode says: “This text isn’t written there: it comes from a variable or prop. Open it in your editor.”Text that isn’t in the project. Posts from a headless CMS, rows from a database, anything fetched while the site runs.
Text in mixed content, as above.
Text the dev server hasn’t caught up with. If you edit twice quickly, midcode may say “The text in the code doesn’t match the preview. Wait for it to reload and try again.” It checks that the code still says what the canvas showed before it writes.
In a component’s own canvas, a text passed in where the component is used is the page’s, not the component’s: “This text is passed in where the component is used (page.tsx): edit it there.” To make a component’s text settable per use, see Variables.
Replace an image or a video
Select an image, a video, or an element with a background image. The right panel shows it under “Image”, “Video” or “Background image”, with a preview and its path.
Click “Replace…” and choose a file. Or drop a file from Finder onto the preview in the panel (“Drop to replace”).
midcode copies the file into your project and points the element at it.
Select an element that isn’t media itself but has some inside (a hero with a background video) and the panel shows “Media” with “Contains an image”, “Contains a video” or “Contains a background image”. Click “Select” to go to it.
What midcode writes
The file is copied into the folder your site serves as it is: public/, or static/ in SvelteKit. It goes next to the file it replaces when that one is there too, otherwise into images/. Then the reference is rewritten:
-<img src="/images/hero.jpg" alt="The studio at night" className="rounded-2xl" />+<img src="/images/hero-winter.jpg" alt="The studio at night" className="rounded-2xl" />The copy’s name is the file’s name in lowercase, with anything that isn’t a letter, a digit, an underscore, a dot or a hyphen turned into a hyphen.
Hero Winter.JPGbecomeshero-winter.jpg.If a different file with that name is already there, the copy gets a number:
hero-winter-1.jpg. If the very same file is there, it’s reused and nothing is copied.The old file is left where it is.
Both parts are in Publish: the edit, and “Add public/images/hero-winter.jpg”.
The reference is found like a text is. First the element’s own src, then the src given where its component is used (<Image src="/images/hero.jpg" />), then the exact path as a string in the same files a text is searched in: an array of slides, a JSON file.
A background image is found by its path too, including inside url(), and for it the search also reads .css files:
-<section className="bg-[url(/images/grain.png)] bg-cover">+<section className="bg-[url(/images/paper.png)] bg-cover">Images imported in the code
In Next.js, an image can be imported instead of referenced by path:
import portrait from './portrait.jpg'
<Image src={portrait} alt="Ana, the founder" />There’s no path in the code to rewrite, so midcode replaces the file itself: your new image is copied over portrait.jpg. Two rules keep that safe:
The new file must have the same extension. “This image is imported as a .jpg file: pick a .jpg to replace it.”
If two files in the project have that name, midcode won’t choose between them. “Several files in the project are called portrait.jpg. Replace it in your editor.”
Alt text
With an image selected, “Alt text” is under its preview. Type a description and press Enter. An image with no alt text shows “Missing” beside the field: screen readers and search engines read it.
-<img src="/images/team.jpg" className="rounded-2xl" />+<img alt="The team at the studio in Lisbon" src="/images/team.jpg" className="rounded-2xl" />An existing alt is changed in place. A new one is added right after the tag’s name.
Paste and drop
Paste
Copy an image anywhere (a browser, a screenshot, a design tool) or copy files in Finder, then press ⌘V in midcode.
| It goes | When |
|---|---|
| After the selection | An element in a page is selected. |
| On the free canvas | Nothing is selected, or something floating is, in a Next.js App Router project. It lands in the middle of what’s in view. See The free canvas. |
| At the end of the page | Nothing is selected, in any other project. If midcode can’t tell which file the page is, it asks you to select an element first. |
A copied image with no file behind it is saved as pasted-image.png.
Drop
Drag images, SVGs, videos or audio from Finder onto the canvas. Over a frame, a blue line shows where each would land, as when you move an element. Over the empty canvas, they float there in a Next.js App Router project, and go to the end of the page in any other.
Accepted: png, jpg, jpeg, gif, webp, avif, svg, mp4, webm, mov, mp3, wav, ogg, m4a, aac and flac. Several files at once land one after the other.
What a paste or a drop writes
Each file is copied into public/images/, public/videos/ or public/audio/ (in SvelteKit, under static/), with the naming rules above, and an element is written for it:
<img src="/images/team-photo.png" alt="Team photo" className="w-full min-w-0 rounded-2xl object-cover" />
<video src="/videos/reel.mp4" autoPlay muted loop playsInline className="w-full min-w-0 rounded-2xl" />
<audio src="/audio/episode-12.mp3" controls className="w-full" />An image’s first alt text is made from its file name: team-photo.png becomes “Team photo”. Change it to say what the picture shows. In a project without Tailwind 4, the classes carry midcode’s prefix (mid:w-full): see Without Tailwind.
On the free canvas, the element is written with its own width instead (className="w-[640px]"): the image’s real width, up to 800 px.
The “Image”, “Video” and “Audio” items of the Insert panel write the same elements, after asking for a file.
Other filesNext release
Everything above is shown in JSX. Svelte files are edited the same way today. From the next release so are Vue, Astro and HTML files, and the templates a server renders (Blade, Liquid, Twig, ERB and others): text between tags, src and alt. What a template computes ({{ title }}) is treated like {title} in JSX: it isn’t a text in that file. Each stack’s page says what was tried: see What works with what.
In Hugo, Jekyll and Eleventy, headings, paragraphs and list items that come from a Markdown file are found by their words and edited in that file. Only the words change (and a heading’s level, with “Tag” in the right panel): Markdown has no tag to put a class or an attribute on. See Hugo, Jekyll and Eleventy. In a Shopify theme, most texts live in locales/*.json and templates/*.json, where the search above finds them: see Shopify themes.
Limits
One text, one place. A text that’s the same in two places midcode can’t tell apart has to be edited in the code.
The search for a text skips files over 1 MB and texts shorter than 2 characters, and reads at most 8,000 files.
Replacing an imported image in place works for Next.js’s imported images. In other projects midcode looks for the image’s path as text. When there isn’t one, it removes the copy it made and tells you to open the element in your editor.
A file copied into
public/stays there if you undo the edit that used it.A new file always goes to
public/(static/in SvelteKit) and is referenced from the site’s root (/images/hero.jpg). A stack that serves its files from another folder (a folder of plain HTML, Hugo’sstatic/) won’t find it there: after replacing or dropping an image, check that itssrcpoints at where the file is.A background image is replaced where its path is written out: in a class, a
style, or a stylesheet. One that comes from a variable is edited in the code.Remote images (
https://…) can be replaced with a local file, not with another URL. To change a URL, edit it in Code view.