Skip to content

Under developmentMica is experimental and not ready for production. The API and the content format can still change, so read the release notes before you upgrade.Follow the releases.

Docs/Add Mica to an existing site

Build

Add Mica to an existing site

Move Astro content collections into Mica with mica import, keep other collections as they are, and keep the content in a separate repository.

6 min read

This guide is for an Astro site that has content already, usually Markdown files in Astro content collections. You convert the entries once. After that, the website reads them through micaCollections(), and the pages change only a little. A coding agent can do these steps for you: give it mica.build/setup.md.

Start on a clean working tree and a new branch, so that git diff shows each change.

1. Keep a copy of the old site

npx astro build
cp -r dist ../dist-before
cp node_modules/.astro/data-store.json ../data-store-before.json

The data store has every entry after Astro's own loader and schema. mica import reads it in step 4. Astro writes it again when the collections change, so keep this copy.

2. Write the model

Install the packages and run npx mica init, as in Getting started. Then write one Mica collection for each Astro collection that editors will change. Give it the same name, so that getCollection("blog") in your pages keeps working.

Astro schemaMica field
z.string()text(), or textarea() for long plain text
z.string().url(), .email()url(), email()
z.number()number()
z.boolean()boolean()
z.date(), z.coerce.date()date(), stored as "2026-01-15"
z.enum([...])select({ options })
z.array(z.enum([...]))select({ multiple: true, options })
z.array(z.string())tags()
image()media({ accept: "image", alt: "required" })
reference("authors")relation({ collection: "authors" })
z.object({...})object({ fields })
z.array(z.object({...}))repeater({ fields })
.optional()the field without required: true
the Markdown bodyrichText(), usually named body
the entry idan id() field

All options are on Fields. Data that Mica must not edit, such as a long list of places, can stay an Astro collection that a relation points to: see Reference data.

3. Mica collections next to Astro collections

A collection that only developers change, such as documentation, can stay an ordinary Astro collection. Put both in one src/content.config.ts:

import { defineCollection } from "astro:content";
import { glob } from "astro/loaders";
import { micaCollections } from "@micacms/astro";
import { site } from "./mica";

const glossary = defineCollection({ loader: glob({ pattern: "*.md", base: "./src/glossary" }) });

export const collections = { ...micaCollections(site), glossary };

Remove the old definitions of the collections that Mica now has. Use different names: when two collections have the same name, the last one is used, and Astro does not warn. Then run npx astro sync. It writes content/.mica/schema.json, which the import reads.

4. Import the entries

npx mica import --from ../data-store-before.json --map blog.cover.alt=coverAlt --order team=order

Each field of the model takes the value with the same name. The command converts the value: a date to "2026-01-15", a reference to an ID, an image to a copy in the entry's media/ folder, and the Markdown body to the rich-text field body. It makes each ID from the Astro entry id.

  • --map <collection>.<field>=<key> takes a field from another key: a renamed field, the alt text of an image (blog.cover.alt=coverAlt), or the body for a rich-text field with another name (blog.content=@body). It does not reach into the rows of a repeater.
  • --order <collection>=<field> makes the order of a sortable collection from a number field. Then the pages sort with inEditorOrder, and the number field can go.
  • Names after the options import only those collections.

The command lists each key that it did not import, each Astro id that gives the same ID as another, each Astro id that gives no ID, and each image that it did not find. Fix the model or add a --map, delete the collection's folder in content/, and run it again.

5. What does not move as it is

  • MDX components in a body. Markdoc does not run them. Use a Markdoc tag, a field, or keep that collection in Astro.
  • Raw HTML in a body. Markdoc does not render it. Use Markdown, a Markdoc tag, or remove it.
  • Footnotes, images and tables in a body. Markdoc shows a footnote as text: use a paragraph and a link. The editor edits an image on a line of its own and a table without aligned columns. Move each image file into the entry's media/ folder and write its path as media/photo.jpg. An image inside a line of text, or a table with aligned columns, makes the field read-only in the editor.
  • Ids with folders (2024/hello). They become flat IDs, so the page's URL can change. Keep the old URL with Astro redirects.
  • Heading ids and code highlighting. Markdoc does not add them. Add them in your rich-text component if your pages use them.

6. Change the pages, then compare

  • Replace <Content /> from render(entry) with the HTML of await renderRichText(entry.data.body, entry.data). The second argument gives the images of the entry, and Astro makes them smaller.
  • Format and sort dates from the text "2026-01-15".
  • Read an image with alt text as { src, alt }: <Image {...entry.data.cover} />.
  • Sort a sortable collection with .sort(inEditorOrder) from @micacms/astro.

Run npx mica check and npx astro build, and compare dist with ../dist-before. Visitors should see no difference, except what you decided to change.

Content in a separate repository

The website can read its content from a second repository. Then editors and their commits stay out of the code repository. The best way is a Git submodule: Mica works in the content repository and never commits in the website repository. See Content in its own repository.

A second checkout in CI works too, but it is not part of Mica's tests, and the local editor does not know the website repository then. The model uses the path of the checkout, for example defineSite({ root: "./site-content/content" }). After a model change, run npx astro sync with the content repository checked out there, and commit schema.json in the content repository.