framer-core-workflow-a

v2026.09.24

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".

GitHub
Install command
npx skhub add jeremylongshore/framer-core-workflow-a
Markdown
SKILL.md

Framer CMS Plugin — Managed Collections

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-auth setup
  • 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

ErrorCauseSolution
Collection existsDuplicate nameUse getManagedCollection() first
Invalid field typeWrong type stringUse: string, formattedText, number, boolean, date, image, enum, slug
Image upload failedURL not publicEnsure images are publicly accessible
setItems timeoutToo many itemsBatch into chunks of 100

Resources

Next Steps

For code components and overrides, see framer-core-workflow-b.

Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

skills/.curated/framer-core-workflow-a

Default branch

main

Latest commit

e5a6c3b

Tree SHA

c2dc8e8