인터페이스는 현지화되어 있으며, Skill 소개와 설명 문서는 선택한 언어로 AI가 자동 번역합니다. 코드·명령어·경로·전문 용어는 원문을 유지하며 원작자 문서가 우선합니다.
Skills 둘러보기
생산성 및 협업 · SKILL.md

framer-core-workflow-b

framer-core-workflow-b

Execute Framer secondary workflow: Core Workflow B. Use when implementing secondary use case, or complementing primary workflow. Trigger with phrases like "framer secondary workflow", "secondary task with framer".

Independent GitHub snapshot · Automated classification · Original documentation governs

Skill 문서

GitHub

name: framer-core-workflow-b description: 'Execute Framer secondary workflow: Core Workflow B.

Use when implementing secondary use case,

or complementing primary workflow.

Trigger with phrases like "framer secondary workflow",

"secondary task with framer".

' allowed-tools: Read, Write, Edit, Bash(npm:*), Grep version: 1.5.0 license: MIT author: Jeremy Longshore jeremy@intentsolutions.io tags:

  • saas
  • framer
  • components
  • overrides compatibility: Designed for Claude Code

Framer Code Components & Overrides

Examples

Build a fictional staging component with no embedded secrets or visitor data, test its publish behavior under a limited role, and verify rollback removes the canary without changing production content.

Overview

Build code components with property controls and code overrides for Framer sites. Components are custom React rendered on the canvas. Overrides modify existing layer behavior (animations, interactions) without changing the component.

Prerequisites

  • Framer project open in editor
  • Understanding of React and Framer Motion

Instructions

Step 1: Code Component with Property Controls

import { addPropertyControls, ControlType } from 'framer';
import { useRef, useEffect, useState } from 'react';

interface Props { target: number; duration: number; suffix: string; fontSize: number; color: string; }

export default function AnimatedCounter({ target = 1000, duration = 2, suffix = '+', fontSize = 48, color = '#000' }: Props) {
  const ref = useRef(null);
  const [count, setCount] = useState(0);
  const [started, setStarted] = useState(false);

  useEffect(() => {
    const observer = new IntersectionObserver(([e]) => { if (e.isIntersecting) setStarted(true); }, { threshold: 0.5 });
    if (ref.current) observer.observe(ref.current);
    return () => observer.disconnect();
  }, []);

  useEffect(() => {
    if (!started) return;
    const inc = target / (duration * 60);
    let val = 0;
    const timer = setInterval(() => {
      val += inc;
      if (val >= target) { setCount(target); clearInterval(timer); }
      else setCount(Math.floor(val));
    }, 1000 / 60);
    return () => clearInterval(timer);
  }, [started, target, duration]);

  return <div ref={ref} style={{ fontSize, fontWeight: 700, color, textAlign: 'center' }}>{count.toLocaleString()}{suffix}</div>;
}

addPropertyControls(AnimatedCounter, {
  target: { type: ControlType.Number, title: 'Target', defaultValue: 1000 },
  duration: { type: ControlType.Number, title: 'Duration (s)', defaultValue: 2 },
  suffix: { type: ControlType.String, title: 'Suffix', defaultValue: '+' },
  fontSize: { type: ControlType.Number, title: 'Font Size', defaultValue: 48 },
  color: { type: ControlType.Color, title: 'Color', defaultValue: '#000' },
});

Step 2: Data-Fetching Component

import { addPropertyControls, ControlType } from 'framer';
import { useEffect, useState } from 'react';

export default function DataList({ apiUrl = 'https://jsonplaceholder.typicode.com/posts', limit = 5 }) {
  const [items, setItems] = useState<any[]>([]);
  useEffect(() => {
    fetch(apiUrl).then(r => r.json()).then(d => setItems(d.slice(0, limit))).catch(() => {});
  }, [apiUrl, limit]);
  return (
    <ul style={{ listStyle: 'none', padding: 0 }}>
      {items.map((item, i) => <li key={i} style={{ padding: '8px 0', borderBottom: '1px solid #eee' }}>{item.title || item.name}</li>)}
    </ul>
  );
}

addPropertyControls(DataList, {
  apiUrl: { type: ControlType.String, title: 'API URL', defaultValue: 'https://jsonplaceholder.typicode.com/posts' },
  limit: { type: ControlType.Number, title: 'Limit', defaultValue: 5 },
});

Step 3: Code Overrides

// overrides.tsx — apply to any layer via properties panel
import { Override } from 'framer';

export function FadeInOnScroll(): Override {
  return { initial: { opacity: 0, y: 20 }, whileInView: { opacity: 1, y: 0 }, transition: { duration: 0.6 }, viewport: { once: true } };
}

export function MagneticHover(): Override {
  return { whileHover: { scale: 1.05 }, whileTap: { scale: 0.95 }, transition: { type: 'spring', stiffness: 400, damping: 17 } };
}

export function TypewriterText(): Override {
  return {
    initial: { width: 0 },
    animate: { width: '100%' },
    transition: { duration: 2, ease: 'linear' },
    style: { overflow: 'hidden', whiteSpace: 'nowrap' },
  };
}

Step 4: Plugin Creating Code Files

import { framer } from 'framer-plugin';

async function createOverrideFile() {
  const codeFile = await framer.createCodeFile({ name: 'Animations', language: 'tsx' });
  await codeFile.setFileContent(`
import { Override } from 'framer';
export function FadeIn(): Override {
  return { initial: { opacity: 0 }, animate: { opacity: 1 }, transition: { duration: 0.8 } };
}`.trim());
  framer.notify('Override file created');
}

Output

  • Animated components with property controls
  • Data-fetching components for external APIs
  • Reusable code overrides for animations
  • Programmatic code file creation

Error Handling

ErrorCauseSolution
Component blankRuntime errorCheck Framer browser console
Controls not showingMissing addPropertyControlsCall after component export
Override not applyingWrong exportMust return Override type
Fetch failsCORSUse CORS-enabled API

Resources

Next Steps

For common errors, see framer-common-errors.

저장한 Skills