Subchapter 7.33
references/cms/INSTRUCTIONS.mdMarkdown10 KBView on GitHub
The data machinery ships as files — typed reads/writes over ANY collection, hooks, and the
normalization layer (dates → ISO strings, wix:image:// → https URLs), correct end-to-end.
CMS is schema-generic, so unlike other verticals there are no fixed pages: the seed plan
(plan.json) is the site’s content model, and — a listing
surface and an item page per content collection, plus the home page and the brand. You never
write data-access logic; you never skip designing.
Don’t read the shipped files — this table and the contracts below are everything you need. Open a shipped file’s source only on a real fallback (runtime error / uncovered field).
| file | what it is |
|---|---|
wix/config.ts · wix/sdk.ts · wix/media.ts · wix/money.ts | shared auth seam + helpers (deploy configures; nothing to set) |
wix/cms/types.ts | the DTOs (CmsItem, CmsFilter, CmsSort, CmsQuery, CmsPage) — contracts below |
wix/cms/items.ts | queryItems, getItemById, getItemBy, countItems, insertItem, updateItem, patchItemFields, removeItem |
hooks/cms/useCollection.ts | list state + skip paging — contract below |
hooks/cms/useItem.ts | one item by _id or slug field — contract below |
styles/global.css | the design system: Tailwind v4 + the @theme token block (shared across verticals) |
There are no shipped components — the data layer and hooks are the machinery; every component is yours.
Astro stack additionally gets:
| file | what it is |
|---|---|
layouts/SiteLayout.astro | site chrome — yours to brand (keep the seo-tags slot + global.css import). If another vertical is also deployed, its layout won — add your content nav links there |
There are no shipped pages — you author them (below).
Read the seed plan first: its collections, field keys, and permissions are the contract your pages bind to (collection ids and field keys verbatim).
useCollection
(or SSR via queryItems alone when the page needs no interactivity).slug field — your detail layout on the DTO
(SSR fetch via getItemBy), with a real 404 on miss.Plus the theme (@theme block, one edit) and the chrome (SiteLayout, one pass).
Style everything with Tailwind utilities on the tokens.
// CmsItem — display-ready, fields FLAT on the item (never item.data.*):
// { _id, _createdDate?, _updatedDate?, _owner?, ...fields }
// TEXT/URL/EMAIL → string · NUMBER → number · BOOLEAN → boolean
// DATE/DATETIME → ISO string (render: new Date(iso).toLocaleDateString())
// IMAGE → resolved https URL (straight into <img src>; guard the absent case)
// RICH_TEXT → the stored HTML string (render via set:html on a wrapper you control)
// REFERENCE/MULTI_REFERENCE → id(s); full CmsItem(s) when queried with include
// queryItems(collectionId, { filters?, sort?, limit?, skip?, include?, withTotal? })
// → { items: CmsItem[], hasNext, total } // filters: [{ field, op, value }]
// ops: eq ne gt ge lt le contains startsWith hasSome hasAll isEmpty isNotEmpty
// DATE comparands must be Date objects (an ISO string matches nothing)
// getItemBy(collectionId, field, value) → CmsItem | null // slug routing
// getItemById(collectionId, id) → CmsItem | null
// countItems(collectionId, filters?) → number
// useCollection(collectionId, { filters?, sort?, limit?, include?, initialItems?, initialHasNext? })
// → { items: CmsItem[]|null /* null = loading → skeletons */,
// hasNext, loadMore(), loadingMore, error } // changing filters/sort refetches
// useItem(collectionId, { id } | { by: { field, value } }, { initialItem? })
// → { item: CmsItem|null, notFound, error } // notFound → your 404/miss state
// Writes (only when the collection's permissions allow the caller):
// insertItem(collectionId, data) // dates as Date objects; never set _owner
// patchItemFields(collectionId, id, fields) // the safe partial change
// updateItem(collectionId, item) // REPLACES the whole item — see hard rules
// removeItem(collectionId, id)Set the @theme tokens (one edit); brand SiteLayout.astro (one pass) and add one nav
link per content surface.
Author your pages under src/pages/ — SSR fetch in the frontmatter, DTO props to your
islands (client:load; a page with no interactivity needs no island at all). Listing:
---
import SiteLayout from "../layouts/SiteLayout.astro";
import { queryItems } from "../wix/cms/items";
import type { CmsPage } from "../wix/cms/types";
let page: CmsPage = { items: [], hasNext: false, total: null };
try {
page = await queryItems("recipes", { sort: [{ field: "publishDate", direction: "desc" }] });
} catch {
// an unguarded SSR throw truncates the response mid-stream
}
---Item page (src/pages/recipes/[slug].astro): getItemBy("recipes", "slug", Astro.params.slug!)
in the frontmatter; return new Response(null, { status: 404 }) on null. SEO is plain
<title>/<meta name="description"> from the DTO via the layout props — CMS collections
have no owner-editable SEO item type, so don’t copy another vertical’s
wixMetadata/<SEO.Tags> wiring.
Author your surfaces in as few messages as possible — batch multiple Writes per message (components and pages are independent files).
Import ./styles/global.css once at the app entry (needs @tailwindcss/vite in the vite
plugins — deploy added the dep). Routes are yours: a list route per collection on
useCollection, a detail route on useItem with by: { field: "slug", value }.
@wix/data directly in your components, never hand-build a static.wixstatic.com
URL._id, fields are flat — item.id is undefined; item.data.title is the
REST shape and reads undefined.read must be
ANYONE) — fix the seed, never reach for auth.elevate (it doesn’t exist on this path).
On a member-scoped collection an empty anonymous read is the gate working, not a bug.updateItem REPLACES the whole item — omitted fields are wiped. Spread the full item,
or use patchItemFields. Date fields on any write are Date objects — a round-tripped ISO
string is silently stored as text and breaks date queries.references/shared/CUSTOM_OPERATIONS.md: validate the caller and
input in a narrow server endpoint before elevating one documented operation. Never call a
privileged Media API directly from a client island.plan.capabilities.mediaUpload; Fast deploys the endpoint, client helper, dependencies, and
generated policy module. Your CMS surface calls uploadMedia(policyId, file) and stores the
returned Wix Media reference. Do not author another upload endpoint for that flow.set:html (Astro) /
dangerouslySetInnerHTML (React) on a wrapper; never interpolate it as text.include — don’t render an id as
content.<img>.@theme tokens; no parallel theme files, no hardcoded palettes.Give the owner the dashboard link — the deploy step’s JSON printed dashboardUrl; append
/wix-cms for the collections area (items, fields, and More Actions → Permissions &
Privacy live there).
Per seed/SEED.md — plain-data plan.json into seed-cms.mjs from the project root. The
plan is the content model your pages bind to: design collections that exercise the UI
(a slug field per detail page, an IMAGE field with a verified imageUrl per item, a DATE
field when the content is chronological).
wix:image://); absent images fall back
gracefully.[object Object]).