Use when defining database schemas, creating data collections, or setting up structured data storage. Triggers include CMS, collection, database, schema, data model, fields, relationships, permissions, data structure, entity definition, data extension.
Creates CMS data collections for Wix CLI apps. The data collections extension allows your app to automatically create CMS collections when it's installed on a site. Collections store structured data that can be accessed from dashboard pages, site pages, backend code, and external applications.
Important: This extension automatically enables the site's code editor, which is required for the Wix Data APIs to work. Without this extension, apps using Data APIs would need the Wix user to manually enable the code editor on their site, which isn't guaranteed. With the data collections extension, your app can reliably use Data APIs to read and write data in the collections.
App namespace is REQUIRED for data collections to work. The namespace scopes your collection IDs to prevent conflicts between apps.
If app namespace is provided in the prompt:
<actual-namespace>/collection-suffixIf app namespace is NOT provided:
<app-namespace> in all code examples: <app-namespace>/collection-suffix<app-namespace> with your actual app namespace from Wix Dev Center"idSuffix): Use just the suffix, e.g., "products""<app-namespace>/products" — MUST match idSuffix exactly (case-sensitive, no camelCase/PascalCase transformation)referencedCollectionId: Use the idSuffix only (not the full scoped ID) — the system resolves it automaticallyidSuffix is "product-recommendations", API calls use "<app-namespace>/product-recommendations" NOT "<app-namespace>/productRecommendations"In Wix CLI apps, all CMS collections are defined in a single file:
File: src/extensions/data/extensions.ts
This file uses the extensions.genericExtension() pattern from @wix/astro/builders to register all collections:
import { extensions } from "@wix/astro/builders";
export const dataExtension = extensions.genericExtension({
compId: "{{GENERATE_UUID}}",
compName: "data-extension",
compType: "DATA_COMPONENT",
compData: {
dataComponent: {
collections: [
// All collections defined here
],
},
},
});
CRITICAL: The compId must be a unique, static UUID string. Generate a fresh UUID v4 for each app - do NOT use randomUUID() or copy UUIDs from examples. After creating or modifying this file, follow wix-cli-extension-registration for UUID generation and to register the extension in src/extensions.ts (required for collections to work).
Key points:
extensions.genericExtension() from @wix/astro/buildersidSuffix, displayName, displayField, fields, dataPermissions, and optionally initialDatadisplayField specifies which field the CMS displays when referencing items in this collection from other collections| Type | Description | Use Case |
|---|---|---|
TEXT | Single-line text | Names, titles |
RICH_TEXT | Formatted HTML text | Blog content |
RICH_CONTENT | Rich content with embedded media | Complex blog posts |
NUMBER | Decimal numbers | Prices, quantities |
BOOLEAN | True/false | Toggles, flags |
DATE | Date only | Birthdays |
DATETIME | Date with time | Timestamps |
TIME | Time only | Schedules |
IMAGE | Single image | Thumbnails |
DOCUMENT | File attachment | PDFs |
VIDEO | Video file | Media |
AUDIO | Audio file | Podcasts |
MEDIA_GALLERY | Multiple media | Galleries |
REFERENCE | Link to one item | Author → User |
MULTI_REFERENCE | Link to many items | Post → Tags |
ADDRESS | Structured address | Locations |
URL | URL validation | Links |
PAGE_LINK | Link to Wix page | Internal navigation |
LANGUAGE | Language code | Multi-language content |
OBJECT | JSON object | Flexible data |
ARRAY | Array of values | Generic arrays |
ARRAY_STRING | Array of strings | Tags list |
ARRAY_DOCUMENT | Array of documents |
CRITICAL: OBJECT fields require objectOptions. When using type: "OBJECT", you MUST include the objectOptions property — the API will reject OBJECT fields without it. Use an empty object {} if you don't need schema validation:
{
"key": "settings",
"displayName": "Settings",
"type": "OBJECT",
"objectOptions": {}
}
For structured objects, define nested fields inside objectOptions.fields:
{
"key": "triggerRules",
"displayName": "Trigger Rules",
"type": "OBJECT",
"objectOptions": {
"fields": [
{ "key": "url", "displayName": "URL Condition", "type": "TEXT" },
{
"key": "scrollDepth",
"displayName": "Scroll Depth %",
"type": "NUMBER"
},
{ "key": "dateStart", "displayName": "Start Date", "type": "DATE" }
]
}
}
{
"key": "email",
"displayName": "Email Address",
"type": "TEXT",
"required": true,
"unique": true,
"defaultValue": null,
"description": "User's primary email"
}
| Property | Description |
|---|---|
key | Field identifier (camelCase) |
displayName | Label shown in CMS |
type | Field data type |
required | Must have value |
unique | No duplicates allowed |
defaultValue | Initial value |
description | Help text |
lowerCamelCase, ASCII only (e.g., productName, isActive, createdAt)idSuffix): lower-kebab-case or lower_underscore (e.g., product-categories, blog_posts)"Product Name", "Is Active")Every collection includes: _id, _createdDate, _updatedDate, _owner
Access levels control who can read, create, update, and delete items in collections.
| Level | Description |
|---|---|
UNDEFINED | Not set (inherits defaults) |
ANYONE | Public access (including visitors) |
SITE_MEMBER | Any signed-in user (members and collaborators) |
SITE_MEMBER_AUTHOR | Signed-in users, but members only access own items |
CMS_EDITOR | Site collaborators with CMS Access permission |
PRIVILEGED | CMS administrators and privileged users |
Common patterns:
read: ANYONE, write: PRIVILEGEDread: SITE_MEMBER, write: SITE_MEMBER_AUTHORread: ANYONE, write: CMS_EDITORread: PRIVILEGED, write: PRIVILEGEDPermission hierarchy (most to least restrictive): PRIVILEGED > CMS_EDITOR > SITE_MEMBER_AUTHOR > SITE_MEMBER > ANYONE > UNDEFINED
CRITICAL: Permissions must match where and how the data is accessed. The consumer of the data determines the minimum permission level — setting permissions more restrictive than the access context will cause runtime failures (empty results or permission-denied errors).
Determine permissions by asking: "Who interacts with this data, and from where?"
| Access Context | Who Sees / Uses It | Implication |
|---|---|---|
Site Widget (SITE_WIDGET) | Any site visitor (public) | Reads must be ANYONE. If the widget accepts input (e.g., reviews, submissions), inserts must also be ANYONE or SITE_MEMBER. |
| Embedded Script | Any site visitor (public) | Same as site widget — reads must be ANYONE. Writes depend on whether visitors can submit data. |
Dashboard Page (DASHBOARD_PAGE) | Site owner / collaborators only | Can use CMS_EDITOR or PRIVILEGED for all operations since only authorized users access the dashboard. |
| Backend code (site-side) | Runs in visitor context | If called from page code or site-side modules, the caller has visitor-level permissions — data must be readable/writable at the appropriate public level. |
| Backend code (elevated) | Runs with auth.elevate() from @wix/essentials | Can bypass permissions, but the collection still needs correct defaults for any non-elevated callers. |
How to apply this:
itemRead must be ANYONE (because the widget is public).itemRead: ANYONE (widget displays it) but itemInsert: CMS_EDITOR (only dashboard users add items). Each operation is independent.Examples by blueprint type:
itemRead: ANYONE, itemInsert: CMS_EDITOR, itemUpdate: CMS_EDITOR, itemRemove: CMS_EDITORitemRead: ANYONE, itemInsert: ANYONE, itemUpdate: CMS_EDITOR, itemRemove: CMS_EDITORitemRead: SITE_MEMBER, itemInsert: SITE_MEMBER, itemUpdate: SITE_MEMBER_AUTHOR, itemRemove: CMS_EDITORitemRead: CMS_EDITOR, itemInsert: CMS_EDITOR, itemUpdate: CMS_EDITOR, itemRemove: CMS_EDITORAnti-pattern: Setting itemRead: PRIVILEGED on a collection that a site widget queries — the widget will return empty results for all visitors because they lack privileged access.
One-to-One / Many-to-One (REFERENCE):
{
"key": "category",
"displayName": "Category",
"type": "REFERENCE",
"referenceOptions": {
"referencedCollectionId": "categories"
}
}
Many-to-Many (MULTI_REFERENCE):
{
"key": "tags",
"displayName": "Tags",
"type": "MULTI_REFERENCE",
"multiReferenceOptions": {
"referencedCollectionId": "tags"
}
}
CRITICAL Constraints:
referencedCollectionId MUST be the idSuffix of another collection in the same planCollections support three operation types for modifying collections:
Creates a new collection or replaces an existing one with the same idSuffix.
Use when: Creating a new collection or completely replacing an existing collection.
Modifies an existing collection by merging new data with existing fields.
Use when: Adding fields, updating permissions, or modifying collection properties.
Behavior:
Removes a collection from the app.
Use when: Removing a collection that is no longer needed.
Behavior:
src/extensions/data/extensions.tsMerge logic: Operations are applied using a Map keyed by idSuffix. Existing collections are loaded into the Map first, then each operation modifies it: INSERT sets/replaces, UPDATE merges with existing (or creates if missing), DELETE removes. The final Map values become the output collections.
Changes to your data collections extension require releasing a new major version of your app. When a user updates to the new major version, their collections are updated as follows:
Important notes:
initialData is ignored during updates.CRITICAL: NEVER create CMS collections to store configuration for embedded scripts.
All embedded script configuration must go through embedded script parameters (embeddedScriptParameters), not CMS collections.
DO NOT create collections for:
Examples of configuration (use parameters, NOT collections):
CMS collections should ONLY be created for:
Decision Rule:
CRITICAL: Site widgets have a built-in settings panel (panel.tsx) that handles ALL widget configuration.
For SITE_WIDGET-only blueprints (no DASHBOARD_PAGE or other extensions):
collections: [] (empty array)Configuration handled by widget panel (NOT collections):
Only create collections for SITE_WIDGET when ALL of these conditions are met:
Don't create aggregated fields. If you have individual rating fields, calculate averages dynamically — don't store computed values like averageRating.
Don't duplicate configuration. If embedded script parameters already hold { headline, color }, don't also create a CMS collection with the same data.
Don't create single-value collections. A collection with one field like { theme: "dark" } should be an embedded script parameter or widget setting instead.
Each item in initialData must match the collection schema exactly:
lowerCamelCase and match the schemaTEXT → string, NUMBER → number, BOOLEAN → booleanDATE/DATETIME → use { "$date": "2024-01-15T10:30:00.000Z" } formatREFERENCE → provide the idSuffix of the referenced collectionRequest: "Create a collection for handling fees with example data"
Generated file: src/extensions/data/extensions.ts
import { extensions } from "@wix/astro/builders";
export const dataExtension = extensions.genericExtension({
compId: "{{GENERATE_UUID}}",
compName: "data-extension",
compType: "DATA_COMPONENT",
compData: {
dataComponent: {
collections: [
{
schemaUrl: "https://www.wix.com/",
idSuffix: "additional-fees",
displayName: "Additional Fees",
displayField: "title",
fields: [
{ key: "title", displayName: "Fee Title", type: "TEXT" },
{ key: "amount", displayName: "Fee Amount", type: "NUMBER" },
],
dataPermissions: {
itemRead: "ANYONE",
itemInsert: "PRIVILEGED",
itemUpdate: "PRIVILEGED",
itemRemove: "PRIVILEGED",
},
initialData: [
{ title: "Handling Fee", amount: 5 },
{ title: "Gift Wrapping", amount: 3.5 },
],
},
],
},
},
});
Request: "Create collections for products and categories with relationships"
Collections:
categories - Category definitionsproducts - Products that reference categoriesBoth collections are defined in the same src/extensions/data/extensions.ts file, with the products collection using a REFERENCE field to link to categories. See the extension template for a complete multi-collection example.
Soft Delete: Add isDeleted (BOOLEAN, default: false)
Status/Workflow: Add status (TEXT) with values like draft/pending/published
URL Slug: Add slug (TEXT, unique) for SEO-friendly URLs
Owner Tracking: Add createdBy (REFERENCE → custom collection, not Members)
Note: For owner tracking, create a custom collection for users rather than referencing Wix Members directly.
src/extensions/data/extensions.ts| File collections |
ANY | Any type | Most flexible |