framer-cms

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Framer CMS — Server API Skill

Framer CMS — Server API 技能

Manage Framer CMS content programmatically via the
framer-api
npm package. Push articles, upload images, create collections, and publish/deploy — all from the terminal, no Framer app needed.
通过
framer-api
npm 包以编程方式管理 Framer CMS 内容。无需打开 Framer 应用,直接在终端即可完成推送文章、上传图片、创建集合、发布/部署站点等操作。

First-time setup (onboarding)

首次设置(新手引导)

If this is the first time the user uses this skill in a project, run the onboarding flow described in
references/onboarding.md
.
Quick check: Look for
FRAMER_PROJECT_URL
and
FRAMER_API_KEY
in the user's
.env
file or environment. If missing, onboard.

如果用户是首次在项目中使用该技能,请运行
references/onboarding.md
中描述的新手引导流程。
快速检查: 查看用户
.env
文件或环境变量中是否存在
FRAMER_PROJECT_URL
FRAMER_API_KEY
,如果缺失则启动引导流程。

How it works

工作原理

This skill uses the Framer Server API (
framer-api
npm package) which connects to Framer projects via WebSocket using an API key. It provides full CMS CRUD, image uploads, publishing, and deployment.
Important: The
framer-api
package must be installed in the project. If not present, run:
bash
npm i framer-api
All operations use ES module scripts (
.mjs
files) with this connection pattern:
javascript
import { connect } from "framer-api"

const framer = await connect(process.env.FRAMER_PROJECT_URL, process.env.FRAMER_API_KEY)
try {
  // ... operations ...
} finally {
  framer.disconnect()
}

该技能使用 Framer Server API
framer-api
npm 包),通过 API 密钥以 WebSocket 连接到 Framer 项目,提供完整的 CMS CRUD、图片上传、发布和部署能力。
重要提示: 项目中必须安装
framer-api
包,如果不存在,请运行:
bash
npm i framer-api
所有操作都使用 ES module 脚本
.mjs
文件),遵循如下连接模式:
javascript
import { connect } from "framer-api"

const framer = await connect(process.env.FRAMER_PROJECT_URL, process.env.FRAMER_API_KEY)
try {
  // ... operations ...
} finally {
  framer.disconnect()
}

Available operations

可用操作

CMS Collections

CMS 集合

OperationMethodNotes
List collections
framer.getCollections()
Returns all CMS collections
Get one collection
framer.getCollection(id)
By collection ID
Create collection
framer.createCollection(name)
Creates empty collection
Get fields
collection.getFields()
Field definitions (name, type, id)
Add fields
collection.addFields([{type, name}])
Add new fields to collection
Remove fields
collection.removeFields([fieldId])
Delete fields by ID
Reorder fields
collection.setFieldOrder([fieldIds])
Set field display order
操作方法备注
列出集合
framer.getCollections()
返回所有 CMS 集合
获取单个集合
framer.getCollection(id)
通过集合 ID 获取
创建集合
framer.createCollection(name)
创建空集合
获取字段
collection.getFields()
字段定义(名称、类型、ID)
添加字段
collection.addFields([{type, name}])
向集合添加新字段
移除字段
collection.removeFields([fieldId])
通过 ID 删除字段
重排字段
collection.setFieldOrder([fieldIds])
设置字段展示顺序

CMS Items (articles, entries)

CMS 条目(文章、内容项)

OperationMethodNotes
List items
collection.getItems()
All items with field data
Add/upsert items
collection.addItems([{slug, fieldData}])
Create or update by ID
Update item
item.setAttributes({slug?, fieldData?})
Partial updates supported
Delete item
item.remove()
Single item
Bulk delete
collection.removeItems([itemIds])
Multiple items
Reorder items
collection.setItemOrder([itemIds])
Set display order
操作方法备注
列出条目
collection.getItems()
所有带字段数据的条目
新增/更新条目
collection.addItems([{slug, fieldData}])
通过 ID 创建或更新
更新条目
item.setAttributes({slug?, fieldData?})
支持部分更新
删除条目
item.remove()
删除单个条目
批量删除
collection.removeItems([itemIds])
删除多个条目
重排条目
collection.setItemOrder([itemIds])
设置条目展示顺序

Field data format

字段数据格式

When creating/updating items, field data is keyed by field ID (not name):
javascript
const fields = await collection.getFields()
const titleField = fields.find(f => f.name === "Title")

await collection.addItems([{
  slug: "my-article",
  fieldData: {
    [titleField.id]: { type: "string", value: "My Article Title" },
  }
}])
Supported field types and their value format:
TypeValue formatExample
string
string
{ type: "string", value: "Hello" }
number
number
{ type: "number", value: 42 }
boolean
boolean
{ type: "boolean", value: true }
date
string
(UTC ISO)
{ type: "date", value: "2026-04-06T00:00:00Z" }
formattedText
string
(HTML)
{ type: "formattedText", value: "<h2>Title</h2><p>Text</p>" }
link
string
(URL)
{ type: "link", value: "https://example.com" }
image
ImageAsset
object
See image upload section
enum
string
(case name)
{ type: "enum", value: "Published" }
color
string
(hex/rgba)
{ type: "color", value: "#FF0000" }
file
FileAsset
object
Similar to image
collectionReference
string
(item ID)
{ type: "collectionReference", value: "itemId123" }
multiCollectionReference
string[]
{ type: "multiCollectionReference", value: ["id1","id2"] }
创建或更新条目时,字段数据以 字段 ID(而非名称)作为键:
javascript
const fields = await collection.getFields()
const titleField = fields.find(f => f.name === "Title")

await collection.addItems([{
  slug: "my-article",
  fieldData: {
    [titleField.id]: { type: "string", value: "My Article Title" },
  }
}])
支持的字段类型及其值格式:
类型值格式示例
string
string
{ type: "string", value: "Hello" }
number
number
{ type: "number", value: 42 }
boolean
boolean
{ type: "boolean", value: true }
date
string
(UTC ISO 格式)
{ type: "date", value: "2026-04-06T00:00:00Z" }
formattedText
string
(HTML 格式)
{ type: "formattedText", value: "<h2>Title</h2><p>Text</p>" }
link
string
(URL)
{ type: "link", value: "https://example.com" }
image
ImageAsset
对象
见图片上传章节
enum
string
(枚举项名称)
{ type: "enum", value: "Published" }
color
string
(hex/rgba 格式)
{ type: "color", value: "#FF0000" }
file
FileAsset
对象
与图片类似
collectionReference
string
(条目 ID)
{ type: "collectionReference", value: "itemId123" }
multiCollectionReference
string[]
{ type: "multiCollectionReference", value: ["id1","id2"] }

Images

图片

Upload images from public URLs, then use the returned asset in CMS items:
javascript
const asset = await framer.uploadImage("https://example.com/photo.jpg")
// asset = { id, url, thumbnailUrl }

await item.setAttributes({
  fieldData: {
    [thumbnailField.id]: { type: "image", value: asset.url }
  }
})
从公共 URL 上传图片,然后将返回的资源用于 CMS 条目:
javascript
const asset = await framer.uploadImage("https://example.com/photo.jpg")
// asset = { id, url, thumbnailUrl }

await item.setAttributes({
  fieldData: {
    [thumbnailField.id]: { type: "image", value: asset.url }
  }
})

Publishing & deployment

发布与部署

javascript
// Create a preview deployment
const result = await framer.publish()
// result = { deployment: { id }, hostnames: [...] }

// Promote preview to production
await framer.deploy(result.deployment.id)
Always ask the user before deploying to production. Publishing a preview is safe; deploying is live.
javascript
// 创建预览部署
const result = await framer.publish()
// result = { deployment: { id }, hostnames: [...] }

// 将预览版本升级到生产环境
await framer.deploy(result.deployment.id)
部署到生产环境前务必询问用户意见。 发布预览是安全的,部署操作会将内容正式上线。

Project info & changes

项目信息与变更

javascript
await framer.getProjectInfo()       // { id, name, apiVersion1Id }
await framer.getCurrentUser()       // { id, name, avatar }
await framer.getPublishInfo()       // Current deployment status
await framer.getChangedPaths()      // { added, removed, modified }
await framer.getChangeContributors() // Contributor UUIDs
await framer.getDeployments()       // All deployment history
javascript
await framer.getProjectInfo()       // { id, name, apiVersion1Id }
await framer.getCurrentUser()       // { id, name, avatar }
await framer.getPublishInfo()       // 当前部署状态
await framer.getChangedPaths()      // { added, removed, modified }
await framer.getChangeContributors() // 贡献者 UUID 列表
await framer.getDeployments()       // 所有部署历史

Other operations

其他操作

OperationMethodNotes
Color styles
getColorStyles()
,
createColorStyle()
Design tokens
Text styles
getTextStyles()
,
createTextStyle()
Typography tokens
Code files
getCodeFiles()
,
createCodeFile(name, code)
Custom code overrides
Custom code
getCustomCode()
Head/body code injection
Fonts
getFonts()
Project fonts
Locales
getLocales()
,
getDefaultLocale()
i18n
Pages
createWebPage(path)
,
removeNode(id)
Page management
Screenshots
screenshot(nodeId, options)
PNG buffer of any node
Redirects
addRedirects([{from, to}])
Requires paid plan
Node tree
getNode(id)
,
getChildren(id)
,
getParent(id)
DOM traversal

操作方法备注
颜色样式
getColorStyles()
,
createColorStyle()
设计 token
文本样式
getTextStyles()
,
createTextStyle()
排版 token
代码文件
getCodeFiles()
,
createCodeFile(name, code)
自定义代码覆盖
自定义代码
getCustomCode()
head/body 代码注入
字体
getFonts()
项目字体
多语言
getLocales()
,
getDefaultLocale()
i18n 支持
页面
createWebPage(path)
,
removeNode(id)
页面管理
截图
screenshot(nodeId, options)
任意节点的 PNG 缓冲区
重定向
addRedirects([{from, to}])
需要付费套餐
节点树
getNode(id)
,
getChildren(id)
,
getParent(id)
DOM 遍历

Common workflows

常见工作流

Push a new article to CMS

推送新文章到 CMS

See
references/cms-operations.md
for the full pattern including field resolution, image upload, and error handling.
完整流程(包括字段匹配、图片上传和错误处理)请参考
references/cms-operations.md

Bulk update articles

批量更新文章

javascript
const items = await collection.getItems()
for (const item of items) {
  await item.setAttributes({
    fieldData: {
      [metaField.id]: { type: "string", value: generateMeta(item) }
    }
  })
}
javascript
const items = await collection.getItems()
for (const item of items) {
  await item.setAttributes({
    fieldData: {
      [metaField.id]: { type: "string", value: generateMeta(item) }
    }
  })
}

Publish after CMS changes

CMS 变更后发布

javascript
const changes = await framer.getChangedPaths()
if (changes.added.length || changes.modified.length || changes.removed.length) {
  const result = await framer.publish()
  console.log("Preview:", result.hostnames)
  // Ask user before: await framer.deploy(result.deployment.id)
}

javascript
const changes = await framer.getChangedPaths()
if (changes.added.length || changes.modified.length || changes.removed.length) {
  const result = await framer.publish()
  console.log("预览地址:", result.hostnames)
  // 部署前需询问用户: await framer.deploy(result.deployment.id)
}

Important notes

注意事项

  • API key scope: Each key is bound to one project. For multiple Framer sites, store multiple keys.
  • WebSocket connection: The
    connect()
    call opens a persistent WebSocket. Always call
    disconnect()
    when done, or use
    using framer = await connect(...)
    for auto-cleanup.
  • Field IDs, not names: CMS operations use field IDs. Always call
    getFields()
    first and resolve names to IDs.
  • Image fields: Pass the full
    framerusercontent.com
    URL from
    uploadImage()
    , not the asset ID.
  • Proxy methods: Most methods (getCollections, publish, etc.) are proxied — they don't appear in
    Object.keys(framer)
    but work correctly.
  • Rate limits: No documented rate limits, but avoid hammering. Add small delays for bulk operations (100+ items).
  • formattedText
    fields:
    Accept standard HTML (h1-h6, p, ul, ol, li, a, strong, em, img, blockquote, pre, code, table, etc.).
  • Draft items: Items can have
    draft: true
    — drafts are excluded from publishing.
  • Blog Posts collection: Collections managed by
    "thisPlugin"
    are read-only via the API. Only
    "user"
    managed collections can be modified.
  • API 密钥权限范围: 每个密钥仅绑定一个项目。如果有多个 Framer 站点,请存储多个密钥。
  • WebSocket 连接:
    connect()
    调用会开启持久化 WebSocket 连接。操作完成后务必调用
    disconnect()
    ,或使用
    using framer = await connect(...)
    实现自动清理。
  • 使用字段 ID 而非名称: CMS 操作使用字段 ID,请始终先调用
    getFields()
    将字段名称匹配为 ID。
  • 图片字段: 传入
    uploadImage()
    返回的完整
    framerusercontent.com
    URL,而非资源 ID。
  • 代理方法: 大多数方法(getCollections、publish 等)是代理方法——不会出现在
    Object.keys(framer)
    中,但可以正常使用。
  • 速率限制: 暂无官方公开的速率限制,但请避免频繁请求。批量操作(100+ 条目)时请添加少量延迟。
  • formattedText
    字段:
    支持标准 HTML 标签(h1-h6、p、ul、ol、li、a、strong、em、img、blockquote、pre、code、table 等)。
  • 草稿条目: 条目可设置
    draft: true
    ——草稿内容不会被发布。
  • 博客文章集合:
    "thisPlugin"
    管理的集合通过 API 是只读的,仅
    "user"
    管理的集合可以修改。