Framer CMS Plugin — Managed Collections
SkillDev tools'Execute Framer primary workflow: Core Workflow A.
Use Framer CMS Plugin — Managed Collections in Claude, ChatGPT or Ahel Desktop
Free. Sign in, add Framer CMS Plugin — Managed Collections and connect your AI. About a minute.
Also: Claude Code · Cursor · Codex
Then ask your AI: use the Framer CMS Plugin skill
Details
Instructions available. Your AI can read the instructions. Execution depends on the setup they require.
Account requirements not reviewed. Check the skill instructions before use; Ahel provides instructions and does not run this skill.
No other account needed.
Add Ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
What this skill tells your AI
The instructions your AI receives, as published by jeremylongshore/tons-of-skills-marketplace in skills/.curated/framer-core-workflow-a/SKILL.md and read by Ahel’s review.
Examples
Create a fictional staging collection, approve its fields and publishing audience, then publish one canary entry. Verify a duplicate sync is suppressed and an unapproved destination receives neither content nor visitor data.
Overview
Build a Framer plugin that syncs external data into CMS Managed Collections. Managed Collections are plugin-controlled — your plugin creates the schema and populates items. This is the primary integration pattern for connecting Framer to external CMSes, databases, or APIs.
Prerequisites
- Completed
framer-install-authsetup - Plugin dev server running
- Understanding of Framer CMS concepts
Instructions
Step 1: Create a Managed Collection
// src/App.tsx — CMS sync plugin
import { framer } from 'framer-plugin';
import { useState } from 'react';
framer.showUI({ width: 340, height: 400, title: 'Content Sync' });
export function App() {
const [status, setStatus] = useState('');
const syncCollection = async () => {
setStatus('Fetching data...');
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
const posts = await response.json();
setStatus('Creating collection...');
const collection = await framer.createManagedCollection({
name: 'Blog Posts',
fields: [
{ id: 'title', name: 'Title', type: 'string' },
{ id: 'body', name: 'Body', type: 'formattedText' },
{ id: 'author', name: 'Author', type: 'string' },
{ id: 'slug', name: 'Slug', type: 'slug', userEditable: false },
],
});
setStatus(`Syncing ${posts.length} items...`);
const items = posts.slice(0, 20).map((post: any) => ({
fieldData: {
title: post.title,
body: `<p>${post.body}</p>`,
author: `User ${post.userId}`,
slug: post.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').slice(0, 50),
},
}));
await collection.setItems(items);
setStatus(`Synced ${items.length} posts`);
framer.notify(`Synced ${items.length} blog posts`);
};
return (
<div style={{ padding: 16 }}>
<h3>Blog Post Sync</h3>
<button onClick={syncCollection} style={{ width: '100%', padding: 8 }}>Sync Now</button>
{status && <p style={{ marginTop: 8, fontSize: 13, color: '#666' }}>{status}</p>}
</div>
);
}
Step 2: Handle CMS Field Types
// Framer CMS field types reference
const fields = [
{ id: 'title', name: 'Title', type: 'string' as const },
{ id: 'content', name: 'Content', type: 'formattedText' as const },
{ id: 'price', name: 'Price', type: 'number' as const },
{ id: 'featured', name: 'Featured', type: 'boolean' as const },
{ id: 'publishDate', name: 'Published', type: 'date' as const },
{ id: 'heroImage', name: 'Hero', type: 'image' as const },
{ id: 'category', name: 'Category', type: 'enum' as const, cases: [
{ id: 'tech', name: 'Technology' },
{ id: 'design', name: 'Design' },
]},
{ id: 'slug', name: 'Slug', type: 'slug' as const, userEditable: false },
];
Step 3: Incremental Sync with Change Detection
async function incrementalSync(collection: ManagedCollection, newData: any[]) {
const existing = await collection.getItems();
const existingMap = new Map(existing.map(i => [i.fieldData.slug, i]));
const toUpsert = newData.map(item => {
const match = existingMap.get(item.slug);
return match ? { ...item, id: match.id } : item;
});
await collection.setItems(toUpsert);
}
Step 4: Unmanaged Collection Access
// Read from user-created CMS collections (not plugin-managed)
const collections = await framer.getCollections();
for (const col of collections) {
if (col.type === 'unmanaged') {
const items = await col.getItems();
console.log(`${col.name}: ${items.length} items`);
}
}
Output
- Managed CMS collection with typed fields
- External data synced into Framer CMS
- Incremental sync support
- Image auto-upload from URLs
Error Handling
| Error | Cause | Solution |
|---|---|---|
Collection exists | Duplicate name | Use getManagedCollection() first |
Invalid field type | Wrong type string | Use: string, formattedText, number, boolean, date, image, enum, slug |
Image upload failed | URL not public | Ensure images are publicly accessible |
setItems timeout | Too many items | Batch into chunks of 100 |
Resources
Next Steps
For code components and overrides, see framer-core-workflow-b.
Signals
- GitHub stars
- 3k
- Forks
- 415
- Last commit
- Oct 2026
Advanced
- Item type
- skill
- Key
framer-core-workflow-a- Source
- github.com/jeremylongshore/tons-of-skills-marketplace
github.com/jeremylongshore/tons-of-skills-marketplace
Related picks
Skill · mattpocock
The pick for TypeScripttypescript-pro
Skill · jeffallan
The pick for TypeScriptreact-component-performance
Skill · davila7
The pick for Reactreact-doctor
Skill · millionco
The pick for Reactteach
Skill · mattpocock
More in Dev toolscaveman
Skill · juliusbrussee
More in Dev tools