CMS
Beta
How midcode finds the content lists in your code, JSON and Markdown files, shows them as tables, and writes each change back into the file.
The CMS shows the content your site already keeps in its files as collections: a table of items, with a form for each one. Nothing is stored in midcode. A collection is a list in your code, a JSON file or a folder of Markdown posts, and every change you make is written into that file, as the edit you would have typed.
Use it for what you write yourself and publish with the site: projects, team members, FAQs, posts. What your visitors create while the site runs (accounts, orders, messages) lives in a database: see Database.
Open it with CMS in the top bar: a button at the right in the released version and, from the next release, a tab in the middle beside Design and Code. Esc goes back to the canvas.
What counts as a collection
midcode reads the project’s files, up to 7 folders deep, and lists three shapes:
| Shape | What it looks for |
|---|---|
| A list in a module | A top-level const (exported or not) or an export default whose value is an array of object literals, in a .ts, .tsx, .js, .jsx or .mjs file |
| A JSON file | A .json file whose top level is an array of objects |
| A Markdown folder | A folder with two or more .md / .mdx files that start with front matter, or one file if the folder is called content, posts, blog, articles, news or docs |
Every element of the array has to be an object, and at least one of its properties a plain value. as const and satisfies around the array are fine.
export const projects = [
{
title: 'Oak House',
slug: 'oak-house',
year: 2024,
featured: true,
cover: '/images/oak-house.jpg',
tags: ['residential', 'timber'],
},
{
title: 'River Studio',
slug: 'river-studio',
year: 2023,
featured: false,
cover: '/images/river-studio.jpg',
tags: ['workspace'],
},
]This file shows up as the collection “Projects” (the variable’s name, in words), with two items and six fields. A JSON file or a default export is named after its file; a Markdown folder after the folder.
These are not read: node_modules, public, build folders (.next, dist, out, build), folders that start with a dot, config files (package.json, tsconfig.json, *.config.*, *.d.ts), and any file over 400 KB.
Field types
The type of each field is inferred from the values the items have. The table header shows it beside the field’s name.
| Type | When |
|---|---|
| Text | A string |
| Long text | A string over 90 characters or with a line break, or a field called body, content, description, summary, text, answer, excerpt or bio |
| Number | Every value is a number |
| Switch | Every value is true or false |
| Date | Every string starts with YYYY-MM-DD |
| Image | Every string ends in .png, .jpg, .jpeg, .webp, .avif, .gif or .svg |
| Link | The field is called href, url, link or website, or every string starts with /, http://, https:// or mailto: |
| List | An array of strings |
| Markdown | The body of a Markdown file |
| Code | Anything that isn’t a plain value: an import, a JSX element, a function call, a nested array of objects |
A Code field is shown as it’s written, with “Code, not content: edit it in the file.” It can’t be changed from the CMS.
An object of plain values becomes one field per key. price: { en: 'Free', es: 'Gratis' } shows as price.en and price.es, so a text kept per language is edited like any other. To see every text of the site by language, use Languages.
Edit an item
Pick a collection on the left. The table lists its items; Search filters them.
Click a row. The item opens on the right, one control per field.
Change a field and leave it (or press Enter in a one-line field). The file is written at that moment.
A Switch can be flipped in the table itself. An Image field takes a path, or Choose image… copies a file into your public folder (under /images) and writes its path. A List adds an entry with Enter and, with nothing typed, removes the last one with ⌫. Emptying a Number removes the property from that item.
The file’s path, next to the collection’s name, opens it in your code editor. The line under it, “Use it in a page:”, is the import to paste where you want the list, written with your tsconfig alias when there is one:
import { projects } from '@/content/projects'Add, remove and reorder
New item adds one at the end of the list. It is a copy of the last item’s text with each plain value emptied (
'',0,false,[]) and “Untitled” as its title; a Code value is copied as it is. In a Markdown folder it creates a new file named after the title, with the folder’s fields in its front matter.Delete item, at the bottom of the item’s panel, removes it from the file. In a Markdown folder, the item’s file goes to the Trash.
Drag the handle at the left of a row to reorder. The commas and comments between items stay where they are. Markdown folders have no order to change, and the handle is hidden while you search.
Field adds a property to every item that doesn’t have it, with an empty value of the type you choose: Text, Long text, Number, Switch, Image, Link, Date or List. Field names are letters, numbers and
_.
New collection
New collection, at the bottom of the list, asks for a name and creates content/<name>.ts (src/content/<name>.ts when the project has a src folder) with one item to start from. “Case studies” gives:
export const caseStudies = [
{
title: 'First item',
slug: 'first-item',
},
]midcode doesn’t add the page that shows it. Import the list where you need it, or ask your agent to build the page.
What midcode writes
Only the range that changes. Changing the year of the second project and the title of the first:
{- title: 'Oak House',+ title: 'Oak House, Lisbon', slug: 'oak-house', slug: 'river-studio',- year: 2023,+ year: 2022, featured: false,A string keeps the quote it had (', " or a template literal). A new property or item follows the layout of its neighbours: the same indentation, a trailing comma if the others have one. Adding the field subtitle:
tags: ['residential', 'timber'],+ subtitle: '', },In a JSON file the same edits are written as JSON (double quotes, quoted keys). In a Markdown file, a field is its line of the front matter and the body is everything under it:
----title: Hello+title: Hello again date: 2026-03-01 tags: [news, studio] ---Front matter lists keep their layout: [a, b] stays inline, - item lines stay lines. A value that YAML would misread (a colon, a leading space, true, a number) is written in double quotes.
Every edit is one step of ⌘Z and one entry in Publish, labelled with the field and its new value. Your dev server shows the change on the canvas like any other edit. A text that comes from one of these lists can also be changed by double-clicking it on the canvas: see Text, images and video.
If the file changed meanwhile
The table is a reading of the file at one moment. Before midcode edits or deletes an item of a code or JSON collection, it reads the file again and compares that item with what the table showed. If they differ (you or your agent changed the file), nothing is written, the list is reloaded and midcode says “The file changed outside midcode. The list was reloaded; try again.”
Read the files again, in the collection’s header, reloads everything on demand.
With your agent
With the crosshair of the agent’s prompt on, a click in the CMS hands over what you clicked instead of opening it: a collection (its file, how many items, its fields), an item (which one, in which file), or one field of one item with its current value.
Limits
The CMS is in beta.
A list has to be at the top level of its module. An array inside an object (
export const site = { faqs: [...] }), inside a function or built with.map()is not a collection.JSON files whose top level is an object are not listed, even if they hold lists.
Front matter is read as plain
key: valuelines,[a, b]lists and- itemlists. Nested objects and multi-line strings in front matter are left alone and don’t show as fields.Modules are
.ts,.tsx,.js,.jsxand.mjs. Lists in the script of a.vue,.svelteor.astrofile are not read.Fields can be added, not renamed or removed from every item at once. Do that in the file (Code view), or with your agent.
A deleted Markdown item is in the Trash, not in ⌘Z. Undoing New collection, or a new Markdown item, empties the file it created and leaves the empty file in place.
Items don’t have ids: an item is its position in the array. That’s why an edit is checked against what the table showed.
In a Shopify theme (next release), the theme’s own JSON in
config/,templates/,sections/,locales/,blocks/andassets/is not listed. The store’s content is in Store.
To write content files that the CMS picks up, see Code that midcode can edit.