framer-cms
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFramer CMS — Server API Skill
Framer CMS — Server API 技能
Manage Framer CMS content programmatically via the 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 应用,直接在终端即可完成推送文章、上传图片、创建集合、发布/部署站点等操作。
framer-apiFirst-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.mdQuick check: Look for and in the user's file or environment. If missing, onboard.
FRAMER_PROJECT_URLFRAMER_API_KEY.env如果用户是首次在项目中使用该技能,请运行 中描述的新手引导流程。
references/onboarding.md快速检查: 查看用户 文件或环境变量中是否存在 和 ,如果缺失则启动引导流程。
.envFRAMER_PROJECT_URLFRAMER_API_KEYHow it works
工作原理
This skill uses the Framer Server API ( npm package) which connects to Framer projects via WebSocket using an API key. It provides full CMS CRUD, image uploads, publishing, and deployment.
framer-apiImportant: The package must be installed in the project. If not present, run:
framer-apibash
npm i framer-apiAll operations use ES module scripts ( files) with this connection pattern:
.mjsjavascript
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( npm 包),通过 API 密钥以 WebSocket 连接到 Framer 项目,提供完整的 CMS CRUD、图片上传、发布和部署能力。
framer-api重要提示: 项目中必须安装 包,如果不存在,请运行:
framer-apibash
npm i framer-api所有操作都使用 ES module 脚本( 文件),遵循如下连接模式:
.mjsjavascript
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 集合
| Operation | Method | Notes |
|---|---|---|
| List collections | | Returns all CMS collections |
| Get one collection | | By collection ID |
| Create collection | | Creates empty collection |
| Get fields | | Field definitions (name, type, id) |
| Add fields | | Add new fields to collection |
| Remove fields | | Delete fields by ID |
| Reorder fields | | Set field display order |
| 操作 | 方法 | 备注 |
|---|---|---|
| 列出集合 | | 返回所有 CMS 集合 |
| 获取单个集合 | | 通过集合 ID 获取 |
| 创建集合 | | 创建空集合 |
| 获取字段 | | 字段定义(名称、类型、ID) |
| 添加字段 | | 向集合添加新字段 |
| 移除字段 | | 通过 ID 删除字段 |
| 重排字段 | | 设置字段展示顺序 |
CMS Items (articles, entries)
CMS 条目(文章、内容项)
| Operation | Method | Notes |
|---|---|---|
| List items | | All items with field data |
| Add/upsert items | | Create or update by ID |
| Update item | | Partial updates supported |
| Delete item | | Single item |
| Bulk delete | | Multiple items |
| Reorder items | | Set display order |
| 操作 | 方法 | 备注 |
|---|---|---|
| 列出条目 | | 所有带字段数据的条目 |
| 新增/更新条目 | | 通过 ID 创建或更新 |
| 更新条目 | | 支持部分更新 |
| 删除条目 | | 删除单个条目 |
| 批量删除 | | 删除多个条目 |
| 重排条目 | | 设置条目展示顺序 |
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:
| Type | Value format | Example |
|---|---|---|
| | |
| | |
| | |
| | |
| | |
| | |
| | See image upload section |
| | |
| | |
| | Similar to image |
| | |
| | |
创建或更新条目时,字段数据以 字段 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" },
}
}])支持的字段类型及其值格式:
| 类型 | 值格式 | 示例 |
|---|---|---|
| | |
| | |
| | |
| | |
| | |
| | |
| | 见图片上传章节 |
| | |
| | |
| | 与图片类似 |
| | |
| | |
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 historyjavascript
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
其他操作
| Operation | Method | Notes |
|---|---|---|
| Color styles | | Design tokens |
| Text styles | | Typography tokens |
| Code files | | Custom code overrides |
| Custom code | | Head/body code injection |
| Fonts | | Project fonts |
| Locales | | i18n |
| Pages | | Page management |
| Screenshots | | PNG buffer of any node |
| Redirects | | Requires paid plan |
| Node tree | | DOM traversal |
| 操作 | 方法 | 备注 |
|---|---|---|
| 颜色样式 | | 设计 token |
| 文本样式 | | 排版 token |
| 代码文件 | | 自定义代码覆盖 |
| 自定义代码 | | head/body 代码注入 |
| 字体 | | 项目字体 |
| 多语言 | | i18n 支持 |
| 页面 | | 页面管理 |
| 截图 | | 任意节点的 PNG 缓冲区 |
| 重定向 | | 需要付费套餐 |
| 节点树 | | DOM 遍历 |
Common workflows
常见工作流
Push a new article to CMS
推送新文章到 CMS
See for the full pattern including field resolution, image upload, and error handling.
references/cms-operations.md完整流程(包括字段匹配、图片上传和错误处理)请参考 。
references/cms-operations.mdBulk 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 call opens a persistent WebSocket. Always call
connect()when done, or usedisconnect()for auto-cleanup.using framer = await connect(...) - Field IDs, not names: CMS operations use field IDs. Always call first and resolve names to IDs.
getFields() - Image fields: Pass the full URL from
framerusercontent.com, not the asset ID.uploadImage() - Proxy methods: Most methods (getCollections, publish, etc.) are proxied — they don't appear in but work correctly.
Object.keys(framer) - Rate limits: No documented rate limits, but avoid hammering. Add small delays for bulk operations (100+ items).
- fields: Accept standard HTML (h1-h6, p, ul, ol, li, a, strong, em, img, blockquote, pre, code, table, etc.).
formattedText - Draft items: Items can have — drafts are excluded from publishing.
draft: true - Blog Posts collection: Collections managed by are read-only via the API. Only
"thisPlugin"managed collections can be modified."user"
- API 密钥权限范围: 每个密钥仅绑定一个项目。如果有多个 Framer 站点,请存储多个密钥。
- WebSocket 连接: 调用会开启持久化 WebSocket 连接。操作完成后务必调用
connect(),或使用disconnect()实现自动清理。using framer = await connect(...) - 使用字段 ID 而非名称: CMS 操作使用字段 ID,请始终先调用 将字段名称匹配为 ID。
getFields() - 图片字段: 传入 返回的完整
uploadImage()URL,而非资源 ID。framerusercontent.com - 代理方法: 大多数方法(getCollections、publish 等)是代理方法——不会出现在 中,但可以正常使用。
Object.keys(framer) - 速率限制: 暂无官方公开的速率限制,但请避免频繁请求。批量操作(100+ 条目)时请添加少量延迟。
- 字段: 支持标准 HTML 标签(h1-h6、p、ul、ol、li、a、strong、em、img、blockquote、pre、code、table 等)。
formattedText - 草稿条目: 条目可设置 ——草稿内容不会被发布。
draft: true - 博客文章集合: 由 管理的集合通过 API 是只读的,仅
"thisPlugin"管理的集合可以修改。"user"