Skip to main content
Fimo’s whole point is that content lives in the CMS, not hardcoded in your JSX — so editors can change it in Studio without touching code. You declare schemas (content models), create entries against them, and render them with generated hooks and fimo/ui components.

The flow

1

Write the schema JSON

Create src/schemas/{Uid}.json — PascalCase filename, e.g. BlogPost.json. Add fields like title, body (richtext), and coverImage (media). Don’t declare a slug field — every entry gets one for free.
2

Push it

This generates src/schemas/BlogPost.types.ts and src/schemas/BlogPost.ts (the typed client) and registers the schema with Fimo.
3

Seed entries

4

Render it

Import the generated client from @/schemas/BlogPost and render with fimo/ui components (<Text>, <Image>, <RichText>). Never write const posts = [...] in JSX.

Schema commands

Schema transitions are automatic — adding fields and changing labels preserve content; changing the schema is the migration request (there are no reset or data-loss flags). Run fimo validate after pushing to regenerate the client and verify the whole project.

Choose a text field

string and text both store plain strings and support the same length, regex, filter, and sort behavior. Changing a field between them preserves its values. Use richtext only when the content needs formatting or structured blocks.

Structure pages with blocks

Use blocks for every nested content group. A blocks field is always an array. Set max: 1 for one fixed group, such as SEO metadata. Use schema.fields when every item has the same shape, or schema.oneOf when editors choose a variant for each item.
Entries send fixed groups as arrays too: "seo": [{ "title": "Home" }]. Every block has a stable _key; a oneOf block also carries _type, matching its variant uid. Nested references return { id, documentId } until populated. Nested population and filtering are not supported yet.

Entry commands

Use the singular commands for one entry. Bulk create and update also accept JSON files or stdin.
entries list accepts the same read query surface as the generated hooks — --search, --sort, --where, --limit, --offset, --fields, and --populate. --where supports operators like $eq, $gte, $in, and $contains:

Query entries with SQL

entries query requires content view access and accepts one read-only SELECT over the entries table. Writes and access to other tables are rejected. Common aggregation, JSON, text, date, and window functions are supported. Queries time out after five seconds and return at most 1,000 rows.
Never use SQL for content writes. It would bypass schema validation, locale and reference handling, content-change events, and current or future webhooks. Use the singular or bulk entry mutation commands instead.
SQL exposes the raw representation: document_id is the stable ID used by the CLI, content tools, references, and API. id identifies one exact locale variant. Select document_id AS id when results will feed another Fimo command, and use locale to select a variant.
If a safe read is blocked because a SQL function is unavailable, report the missing function to Fimo support rather than working around the restriction with a write.

Bulk create, update, and delete

Bulk create takes an array of the same bodies accepted by entries create:
Bulk update can apply one regular update body to IDs or to entries matched by --where:
For different changes per entry, pass a JSON array through --file or --stdin:
Each bulk request is limited to 1,000 entries. Missing requested IDs do not fail valid work: the CLI prints a warning, while --json includes a top-level warnings array and the exact IDs in data.meta.missing. Bulk writes apply the singular write path one entry at a time. Recoverable create and update errors report how many earlier entries were already changed.
The exact entry --body shape — where slug goes, how media fields look, and the Tiptap richtext format — is part of the project’s fimo package surface. Load the project fimo skill before constructing an entry payload.

Localized, shared, and singleton content

Schemas are localized by default. Omit --locale for the default locale; pass --locale <locale> (with --document-id on create) for other locales. Use "i18n": { "enabled": false } for content shared across all locales, and "isSingleton": true for a type with exactly one document.

Generated types & hooks

fimo schemas push (and fimo validate) generate a typed client per schema. App code imports from @/schemas/{Uid} — collection hooks like useList() / useInfiniteList(), useGet() for singletons and legacy fixed queries, plus BlogPostResult (reads) and BlogPostInput (writes) types. Never edit the generated .ts files — regenerate them instead. An entry’s id stays the same across locale variants. Pass locale to select a specific variant; entryId identifies only the exact variant returned. documentId remains available as a compatibility alias for id. Reference inputs use the stable identity, for example { "id": author.id }. Generated read and mutation hooks use the active Fimo locale when you omit locale. This includes useCreate(), useUpdate(), and useRemove(). Pass an explicit locale when a mutation must target a different variant.

What’s next

Media & AI generation

Add cover images and video.

Labels, locales & i18n

Wrap static copy in t().

Framework support

Render content with fimo/ui.