Modulify

The CMS

Browse collections, edit rows and add content without touching SQL.

On this page

The CMS is where you read and write the contents of your site database by hand. It reads your real tables, so a collection is a table and an item is a row.

Before you begin

Your project needs a database. A site created now has one from the start. If the CMS tab shows No database, ask the AI to create one first. Site databases covers that step.

Open the CMS

Open a project and pick the CMS icon in the editor's toolbar. It sits in the pill of icons next to Preview, and the active one shows its name beside the icon. The URL becomes /database under your project.

Two sub-tabs sit at the top: Data, which is the browser, and Settings.

Pick a collection

The left sidebar is headed Collections and lists every table in your database, with its row count on the right. Names are title cased and pluralised, so a blog_posts table reads "Blog Posts" and a post table reads "Posts".

Once you have twenty or more collections, a Search collections box appears above the list. It matches on name and schema. With nothing selected, the panel reads Select a table, "Pick one from the sidebar to browse."

Hidden join tables used by multi reference fields are excluded by default. Turn them on in the Settings sub-tab and they appear with a small system badge.

Read the table

Selecting a collection opens the table view. It does not show every column: it picks the most useful ones, usually a title or name column plus created_at and updated_at, up to five in total. Widen the panel or open a row to see the rest.

The toolbar carries a search box placeholdered with the collection name, for example "Search Blog Posts". It matches your term anywhere in the row, so searching a category name finds rows whose category column contains it. Rows load fifty at a time and the footer shows the total count with Page X of Y and arrow buttons when there is more than one page.

An empty collection reads No data, "Entities appear here once they are created." A search with no hits reads No matches with a prompt to clear the search.

Changes made elsewhere

There is no refresh button. While the Data sub-tab is on screen, the CMS reloads the page of rows you are on once a minute, together with its row count, so items that your live site, a teammate or a connected AI client adds or changes show up without a reload. With no search typed, the open collection's count in the sidebar follows that reload, and the whole Collections sidebar is read again about every five minutes. Coming back to the tab reloads it straight away when the last reload was half a minute or more ago.

A reload keeps you on the page you are on, and it only switches collections when the one you had open no longer exists. It skips its turn while you are adding or editing an item, while a save, delete or clear is running, and while a search you typed is about to run, so it never overwrites what you are working on.

Add an item

Click Add in the toolbar. A panel slides in from the right titled after the collection, for example "New Post".

Fields are split into two sections. Basic info holds title, name and slug when the collection has them, in that order. Custom fields holds everything else, followed by any read only columns.

While you are creating a row, typing into title or name fills the slug for you, until you edit the slug yourself. Under a slug field, the CMS shows the full public URL that row will live at, and clicking it copies the URL to your clipboard.

Required fields are marked with an amber star. That comes straight from the column being NOT NULL in the database. Press Create to save. If something is missing or malformed, the field turns red and shows the reason, for example "Required", "Invalid JSON", or "Must be a UUID".

Edit an item

Click any row in the table to open the same panel in edit mode. The header shows the row's title or name, or "Edit Post" when the row has neither.

Change what you need and press Save. id, created_at and updated_at are not editable: they are shown greyed out with a read-only label, because the database maintains them.

The expand button beside Save widens the editor to the full panel. Its tooltip toggles between Full screen and Exit full screen.

Rich text fields

A rich text field, such as the body of a blog post, is edited in place. Its toolbar holds bold, italic, underline and strikethrough, then paragraph, headings 1 to 6 and quote, then bullet and numbered lists, then image, link, code block and table. Hover a button to see its name. A button that is switched off says why, for example inside a code block. Cmd / Ctrl + Z undoes your last change, including tables, code blocks, images and links, and Cmd + Shift + Z on a Mac or Ctrl + Y on Windows redoes it. The undo history starts fresh when you open another item.

Code blocks

The code block button turns the paragraph, heading or list your cursor is in into a code block. Select several paragraphs first to turn them into one block together. On an empty line it starts an empty block, and pressed again inside a block it turns every line back into a paragraph.

Inside a code block:

  • Enter starts a new line in the same block. Tab indents by four spaces, or indents every line of a selection that spans several, and Shift + Tab takes that indent back out.
  • Pasting adds the copied text as plain code, keeping its line breaks and indentation.
  • Pressing Enter three times at the end of the block, or Cmd / Ctrl + Enter anywhere in it, leaves the block and starts a paragraph below.
  • Backspace in an empty block turns it back into a paragraph.
  • The formatting, image, link and table buttons are switched off, because code is kept as plain text.

While your cursor is in a code block, a menu at its top right corner shows its language, Plain text until you pick one. The menu lists Bash, C, C++, C#, CSS, Diff, Dockerfile, Go, GraphQL, HTML, Java, JavaScript, JSON, JSX, Kotlin, Markdown, PHP, Python, Ruby, Rust, SCSS, SQL, Swift, TSX, TypeScript, XML and YAML. A block with a language shows it in its corner, and the next block you start in the same field uses the language you picked last.

Tables

The table button inserts a table with three columns, a header row and two rows below it. It goes below the paragraph, heading or list your cursor is in, or takes the place of an empty paragraph. The button is off while your cursor is inside a code block or a table.

Tab moves to the next cell and Shift + Tab to the previous one, and Tab in the last cell adds a row. The up and down arrows move between rows, and out of the table from its first or last row. Enter starts a new line inside the cell.

While your cursor is in a table, a toolbar appears at its right edge with Insert row above, Insert row below, Insert column left, Insert column right, Header row, Delete row, Delete column and Delete table. Header row makes the first row the table's header, or turns it back into an ordinary row. Deleting the only row or the only column deletes the whole table.

Pasting a copied table into a cell adds its text, never a table inside the table.

Code blocks and tables never merge with the text around them. Backspace at the start of the paragraph after one moves your cursor into it, and at the start of a list or quote after one it turns the first item into a paragraph. Delete at the end of the line before one leaves both as they are, and an empty paragraph next to one is removed instead.

How they look on your site

Your site shows tables and code blocks plainly at first, with no design of their own: no borders, backgrounds, stripes or syntax colors, just your site's fonts and colors. A wide table or a long line of code scrolls sideways instead of breaking the page. The look is yours to choose, in one of two ways:

  • Ask in chat, for example for dark code blocks with a copy button, striped tables, borders on every cell or syntax highlighting.
  • Change the styles yourself. Every rich text field on your site renders through one shared component inside a rich-text wrapper, and chat keeps all of its styles in one stylesheet, so one change applies everywhere. You can read that file in the File Explorer, ask chat which one it is, and edit it in your own repository once you move your code to GitHub.

The language you pick is saved with each code block, as a language- class on the code, and that is what syntax highlighting works from once you add it.

Images and files

An image, gallery or file field uploads what you pick into the CMS folder of Storage, into a folder of the item's own, and stores the file's address in the field. Images you add to a rich text field go to the same folder. Under an image or file field, a line reads Saved in Storage followed by the file's path, such as CMS/posts/42/hero-3f9a1c2b7d4e.png, and under a gallery it shows the folder its images are in. Click it to open that folder in the Storage tab.

The CMS keeps these files in step with your items. Replacing or removing a file deletes the old one when you save the item, a file you upload but do not save is deleted when you close the item, and deleting the item deletes its files. A file stays while another item of the same collection still uses it. The CMS folder is managed by the CMS, so its files cannot be moved, renamed or deleted from the Storage tab.

Duplicate, export or delete an item

The three dots menu in the editor header holds:

  • Duplicate, which opens a new unsaved row prefilled from this one. A slug becomes slug-copy and a title or name becomes "Name (Copy)", with a number appended if that is already taken on the current page.
  • Export as CSV, which downloads just this row. This requires a paid plan and is disabled with a Paid badge otherwise.
  • Delete, which asks "Delete this post?" using the collection's own name, and warns that it is permanent. The item's images and files in Storage are deleted with it, unless another item still uses them.

Multilingual fields

If your site has more than one language and the AI has set the collection up for it, translatable fields collapse into one field with the language code shown next to the label, rather than appearing as separate raw columns. Use the language switcher at the right end of the bar that holds the Data and Settings sub-tabs, or in the editor header when it is full screen, to change which language you are editing.

The base column always holds the default language. An untranslated field shows the default language's text as its placeholder, so you can see what you are translating.

The companion columns behind those fields, and how Modulify works out which languages your site has, are covered in Multiple languages.

Code fields

A code field holds code your site runs exactly as written: an embed from another service, such as a booking widget, a form, a map, a video or a social post, a block of your own HTML and CSS, or a script. Ask for one in chat:

Add an embed field to events so each event can show its own booking widget.

The field opens in a code editor with HTML, CSS and JavaScript highlighting, line numbers, and undo and redo. Tab indents. To leave the field with the keyboard, press Escape and then Tab. The copy button in the field's toolbar copies the whole code, and the line under the field reads "Placed on your live site exactly as written, scripts included. We don't validate what the code does, so only add code you trust."

Switch the toolbar from Code to Preview to run the code in an isolated frame, and the line under the field switches to a note about the preview. The frame has none of your site's styles and no access to your account. Video players, maps and booking widgets, such as Vimeo, Google Maps or Calendly, play in it, but YouTube videos and other embeds locked to your site's address only play on your live site. An empty field reads Nothing to preview.

The CMS refuses to save code that would break itself or your page, such as a <script> tag that is never closed or a <base> tag, and names the problem under the field. Column types lists every message. The check runs on the code you type, so code that chat or an AI client stored earlier never stops you saving the item's other fields.

When you give chat a snippet, it stores it as you wrote it. The only changes it makes are closing a tag you left open and removing a <base> or <plaintext> tag, and it tells you when it does.

In the table, a code field shows the start of its code in a monospaced font. The table never runs it.

On your site, the AI renders a code field through one shared component, and the code runs in the visitor's browser once the page has loaded:

  • Scripts run in order. An external script is waited for before the next one runs, unless it is marked async, and defer and module scripts run after the rest, as on a normal page.
  • A handler the code adds for DOMContentLoaded or the window's load event runs once the code's scripts have finished, and document.write places its output where the script sits.
  • The code runs again when a visitor comes back to the page without a full reload, and posts from X, Instagram and Facebook are drawn again. A module script, or a widget script that refuses to load twice, can stay empty until the visitor reloads the page.
  • Code that throws an error breaks only itself, never the page around it. CSS in a code field still applies to the whole page, so scope it with a class.
  • The code is not part of the HTML your server sends, so search engines and link previews do not see the text inside it. Keep content that matters for search in normal fields.

A code field belongs to one item. A tracker that should run on every page, such as Google Analytics, does not go in the CMS: see Add a third-party tracker.

Collection level actions

The three dots button next to Add applies to the whole collection:

  • Export as CSV, paid plans only.
  • Clear data, which asks "Clear collection data?" and warns that it permanently deletes every item while keeping the collection, its fields and its structure. The collection's images and files in Storage are deleted too. If other collections link to these items, the clear is refused with "Some items are linked from other collections and cannot be cleared. Clear those collections first!"

Both are disabled while the collection is empty or a request is in flight.

Chat can add, change and remove items for you as well, in plain words such as add a product called Linen Shirt priced 40. It goes through the same checks as this tab, so slugs stay unique and multi-reference links stay in sync, and it asks you first before deleting an item.

Settings

The Settings sub-tab holds four groups. Collections has a single switch, Show system collections, described as "Show the hidden collections used by multi-reference fields." Export downloads the whole database. Backups has a View backups button that opens the Database tab of Backups, where the daily database backups are listed, and a Daily backups switch that stops the daily backup of this site when turned off, while manual backups keep working. Both are greyed out on a site on the older database, which is not backed up, and on the free plan they carry a Paid badge and the switch is disabled. Danger zone holds Clear collections, which clears every collection and deletes the CMS folder in Storage with them, and Delete all database backups, which removes every finished backup of the site and leaves your collections untouched. Export and Danger zone are covered in Clearing and exporting, and the backup controls in Backups.

From an AI client over MCP

list_collections, get_collection_schema and list_rows read the CMS, and create_row, update_row and delete_row write to it. They look after an item's files the same way this tab does, and the storage tools can read the CMS folder but not change it. list_reference_options and list_enum_options are worth calling before writing a reference or enum column, since those columns refuse anything outside their allowed set.

Reading needs data:read, ticked by default. Writing needs data:write, which is not. Emptying a collection with clear_collection needs sites:delete.

See MCP tools.

Next

  • Column types explains which editor each column gets.
  • Site databases covers how tables get created in the first place.
  • Storage is where CMS image and file uploads land.