Back to skills
SKILL.md
Framer Core Workflow A
ASecurity'Execute Framer primary workflow: Core Workflow A.
- 2,688 stars
- 0 votes
- 0 copies
- 2 views
- Added May 29, 2026
Works with
Security analysis
100/100npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill framer-core-workflow-a --agent claude-codeAre you the author of Framer Core Workflow A?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/jeremylongshore-framer-core-workflow-a)---
name: framer-core-workflow-a
description: 'Execute Framer primary workflow: Core Workflow A.
Use when implementing primary use case,
building main features, or core integration tasks.
Trigger with phrases like "framer main workflow",
"primary task with framer".
'
allowed-tools: Read, Write, Edit, Bash(npm:*), Bash(npx:*), Grep
version: 1.0.0
license: MIT
author: Jeremy Longshore <jeremy@intentsolutions.io>
tags:
- saas
- framer
- cms
- plugin
compatibility: Designed for Claude Code
---
# Framer CMS Plugin — Managed Collections
## 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-auth` setup
- Plugin dev server running
- Understanding of Framer CMS concepts
## Instructions
### Step 1: Create a Managed Collection
```tsx
// 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
```typescript
// 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
```typescript
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
```typescript
// 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
- [Framer CMS API](https://www.framer.com/developers/cms)
- [Managed Collections](https://www.framer.com/developers/reference/plugins-managed-collection)
## Next Steps
For code components and overrides, see `framer-core-workflow-b`.
Attribution
Comments
Loading comments…