Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Configure Framer local development with hot reload and testing. Use when setting up a development environment, configuring test workflows, or establishing a fast iteration cycle with Framer. Trigger with phrases like "framer dev setup", "framer local development", "framer dev environment", "develop with framer".
.claude/skills/jeremylongshore-framer-local-dev-loop/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 32% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 11% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 25% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 13% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 45% | 0% |
Set up a fast development workflow for Framer plugins and code components with Vite hot-reload, TypeScript, and testing.
framer-install-auth setupbashnpx create-framer-plugin@latest my-plugin cd my-plugin npm install npm run dev # Starts Vite dev server — hot-reloads into Framer editor
Project structure:
my-plugin/
├── src/
│ ├── App.tsx # Plugin UI (React)
│ ├── main.tsx # Entry point
│ └── framer.d.ts # Type definitions
├── package.json
├── vite.config.ts # Vite config with framer-plugin
└── tsconfig.jsonsrc/App.tsx hot-reload instantlytypescript// tests/sync.test.ts — test data transformation outside Framer import { describe, it, expect } from 'vitest'; // Extract pure functions for testability function transformPosts(posts: any[]) { return posts.map(p => ({ fieldData: { title: p.title, body: `<p>${p.body}</p>`, slug: p.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').slice(0, 50), }, })); } describe('CMS Sync', () => { it('should transform posts into CMS items', () => { const posts = [{ title: 'Hello World', body: 'Content here', userId: 1 }]; const items = transformPosts(posts); expect(items[0].fieldData.title).toBe('Hello World'); expect(items[0].fieldData.slug).toBe('hello-world'); expect(items[0].fieldData.body).toContain('<p>'); }); it('should handle slugs with special characters', () => { const posts = [{ title: 'What\'s New in 2025?', body: 'test', userId: 1 }]; const items = transformPosts(posts); expect(items[0].fieldData.slug).toBe('what-s-new-in-2025-'); }); });
bash# Code components are edited directly in Framer editor # For local development of shared component libraries: mkdir framer-components && cd framer-components npm init -y npm install react framer typescript @types/react
tsx// components/Button.tsx — develop locally, paste into Framer import { addPropertyControls, ControlType } from 'framer'; export default function Button({ label = 'Click me', variant = 'primary' }) { const styles = { primary: { background: '#000', color: '#fff' }, secondary: { background: '#eee', color: '#000' }, }; return <button style={{ ...styles[variant], padding: '12px 24px', border: 'none', borderRadius: 8, fontSize: 16, cursor: 'pointer' }}>{label}</button>; } addPropertyControls(Button, { label: { type: ControlType.String, title: 'Label', defaultValue: 'Click me' }, variant: { type: ControlType.Enum, title: 'Variant', options: ['primary', 'secondary'], defaultValue: 'primary' }, });
typescript// server-dev.ts — develop Server API integrations locally import { framer } from 'framer-api'; import 'dotenv/config'; async function dev() { const client = await framer.connect({ apiKey: process.env.FRAMER_API_KEY!, siteId: process.env.FRAMER_SITE_ID!, }); // Test CMS operations const collections = await client.getCollections(); console.log('Collections:', collections.map(c => `${c.name} (${c.type})`)); } dev().catch(console.error);
| Error | Cause | Solution | |-------|-------|----------| | Plugin not showing | Dev server not running | Run npm run dev | | Hot-reload not working | Wrong Vite config | Ensure framer-plugin Vite plugin is configured | | framer undefined in tests | Editor-only API | Mock framer or extract pure functions | | Component type errors | Missing Framer types | Install @types/framer or use framer.d.ts |
See framer-sdk-patterns for production-ready patterns.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 20,430 | 20,069 | -2% | 1 | 1 | 0% | 4,042 | 4,558 | +13% | 0 | 0 | — |
case-02 | fail→pass | 14,831 | 10,221 | -31% | 1 | 1 | 0% | 1,645 | 2,166 | +32% | 0 | 0 | — |
case-03 | pass→pass | 17,496 | 11,185 | -36% | 1 | 1 | 0% | 2,057 | 2,333 | +13% | 0 | 0 | — |
case-04 | pass→pass | 16,115 | 8,039 | -50% | 1 | 1 | 0% | 1,822 | 2,634 | +45% | 0 | 0 | — |
case-05 | pass→pass | 15,550 | 17,529 | +13% | 1 | 1 | 0% | 2,850 | 3,753 | +32% | 0 | 0 | — |
case-06 | fail→pass | 19,851 | 13,823 | -30% | 1 | 1 | 0% | 2,760 | 3,058 | +11% | 0 | 0 | — |
case-07 | pass→pass | 15,736 | 12,896 | -18% | 1 | 1 | 0% | 2,688 | 3,661 | +36% | 0 | 0 | — |
case-08 | pass→pass | 8,779 | 3,031 | -65% | 1 | 1 | 0% | 690 | 1,826 | +165% | 0 | 0 | — |
case-09 | pass→pass | 7,953 | 10,315 | +30% | 1 | 1 | 0% | 1,441 | 2,152 | +49% | 0 | 0 | — |
case-10 | fail→fail | 16,733 | 11,274 | -33% | 1 | 1 | 0% | 2,105 | 3,362 | +60% | 0 | 0 | — |
case-11 | pass→pass | 23,880 | 11,826 | -50% | 1 | 1 | 0% | 3,323 | 3,690 | +11% | 0 | 0 | — |
case-12 | pass→pass | 7,612 | 7,560 | -1% | 1 | 1 | 0% | 1,214 | 1,697 | +40% | 0 | 0 | — |
case-13 | pass→pass | 6,170 | 1,365 | -78% | 1 | 1 | 0% | 975 | 1,460 | +50% | 0 | 0 | — |
case-14 | pass→pass | 15,557 | 2,018 | -87% | 1 | 1 | 0% | 1,934 | 1,616 | -16% | 0 | 0 | — |
case-15 | fail→pass | 15,584 | 10,968 | -30% | 1 | 1 | 0% | 1,980 | 2,479 | +25% | 0 | 0 | — |
case-16 | pass→pass | 17,235 | 8,326 | -52% | 1 | 1 | 0% | 2,242 | 2,904 | +30% | 0 | 0 | — |
case-17 | pass→pass | 9,339 | 5,313 | -43% | 1 | 1 | 0% | 1,833 | 2,344 | +28% | 0 | 0 | — |
case-18 | pass→pass | 10,720 | 6,028 | -44% | 1 | 1 | 0% | 1,796 | 2,339 | +30% | 0 | 0 | — |
case-19 | pass→pass | 4,282 | 7,925 | +85% | 1 | 1 | 0% | 835 | 1,689 | +102% | 0 | 0 | — |
case-20 | pass→pass | 15,826 | 10,762 | -32% | 1 | 1 | 0% | 2,100 | 3,510 | +67% | 0 | 0 | — |
case-21 | pass→pass | 15,500 | 18,182 | +17% | 1 | 1 | 0% | 2,342 | 4,600 | +96% | 0 | 0 | — |
case-22 | pass→pass | 8,428 | 11,564 | +37% | 1 | 1 | 0% | 1,801 | 2,733 | +52% | 0 | 0 | — |
case-23 | pass→pass | 10,647 | 2,354 | -78% | 1 | 1 | 0% | 1,232 | 1,766 | +43% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +13 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.