framer-plugins について
Framer Plugin SDK を使った Framer プラグインの作成、デバッグ、変更を支援する開発リファレンスです。SDK の設定、プラグインモードと API に加え、コミュニティ管理の手順の確認や任意の更新方法を扱います。
主な機能
- 01SDK の設定と移行ガイド
- 02プラグインモードと API リファレンス
- 03CMS コレクションのワークフロー
- 04バージョン更新の確認
- 05任意の上流への貢献
活用シーン
- 01Framer プラグインの新規作成
- 02プラグインの SDK v4 移行
- 03キャンバスまたは CMS の動作をデバッグ
- 04スキルの更新を確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ fredm00n/ framerlabs --skill framer-pluginsインストール後、Claude Code で/framer-pluginsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/api-reference.mdcms-managed-collections.mdmarketplace.mdpatterns.mdpitfalls.md詳細
翻訳後に原文が更新されています。最新の内容は原文をご確認ください。
Framer プラグイン開発ガイド
Framer Plugin SDK のエキスパートとして、Framer プラグインの構築、デバッグ、変更にこのリファレンスを使用してください。プロジェクト固有の上書き設定については、必ずプロジェクトの CLAUDE.md を確認してください。
クイックリファレンス
- SDK パッケージ:
@framer/plugin(v4 以降、Framer 3.0 リリース)。framer-pluginから名称が変更されました。旧名称は npm で非推奨になっているため、使用しないでください。 - スキャフォールディング:
npm create framer-plugin@latest(create-framer-pluginスキャフォルダーは名称を維持し、@framer/pluginをインストールします) - ビルド:Vite +
vite-plugin-framer(名称は変更なし。framer-plugin-toolsのpackCLI も変更なし) - 基本スタイル:
import "@framer/plugin/framer.css"(任意:Inter のみを読み込むにはimport "@framer/plugin/inter.css") - コアのインポート:
import { framer } from "@framer/plugin" - 開発ワークフロー:
npm run dev→ Framer → Developer Tools → Development Plugin
SDK v4(2026-06-16)と最近の v3.x での追加機能
v4.0 自体は、主に名称変更です(バージョン情報は公式変更履歴と照合済み、2026-07-13)。v4 でリリースされた内容:パッケージ名の変更 framer-plugin → @framer/plugin(現在の安定版は 4.0.1。旧パッケージは npm で非推奨。移行は機械的な検索・置換で済み、一般的に使われる API は削除されていません。peerDeps は react/react-dom が ^18.2.0、csstype が ^3.1.1)、スタイルを刷新した framer.css(Framer 3.0 のデザイン:Inter の新しい字形バリエーション、選択色、入力要素のスタイル。アップグレード後はカスタムの入力/選択スタイルを再確認してください。Inter のみを読み込む新しい @framer/plugin/inter.css エクスポート)、WebPageNode.clone()/DesignPageNode.clone()、getLocalizationGroups() の任意のフィルター、新しいフレーム/テキストリンク属性、UnknownNode を受け付けるようになった setParent。
v3.x で追加(2025 年~2026 年初頭)— 公開済みの多くのサンプルより新しく、権限による制限はありません:
framer.navigateTo(nodeId, opts?)(3.7.0)—ノードを選択し、表示範囲に収まるようズームします(opts: { select?=true, zoom? })。インスタンス形式:node.navigateTo()、collectionItem.navigateTo()、codeFile.navigateTo()。挿入/置換の直後に使うのに最適です。framer.setMenu(items)とframer.showContextMenu(items, config)(3.5.2)—ウィンドウのヘッダーにグローバルメニューを表示し、プラグイン UI のどこにでもコンテキストメニューを表示できます。framer.subscribeToOpenCodeFile(cb)(3.7.0)—ユーザーがコードファイルを開いたり切り替えたりすると呼び出されます。- Design Pages(3.8.0):同じ Nodes + Selection API を使って、Design Pages 上のノードを読み取り/作成/編集できます。
- CMS:すべての Collections(プラグイン管理対象だけでなく)にアクセスし、非管理対象のコレクションにも書き込めます(Plugins 3.0、2025 年 3 月)。
framer.createManagedCollection()(3.10.2)、framer.setCloseWarning()(3.10.3)。framer.getManagedCollection()は非推奨です。代わりにgetActiveManagedCollection()を使用してください(非推奨であることは d.ts に記載されていますが、CMS ドキュメントページのサンプルには古い呼び出しが残っています)。
新しいプラグインを始める
npm create framer-plugin@latest→ Vite + React +framer.json+ サンプルのAppをスキャフォールドします。- まずモードを選びます(以下の表を参照)。CMS プラグインでは
configureManagedCollection+syncManagedCollectionが必要です。アセット/挿入プラグインではcanvas(またはimage/editImage)を使用します。 import "@framer/plugin/framer.css"を読み込み、useLayoutEffect内でframer.showUI(...)を呼び出します。- ストレージは早い段階で決めてください。ユーザーごとのシークレットには
localStorage、共有状態にはpluginDataを使用します(以下の判断フローを参照)。
既存のプラグインをコピーして新しいプラグインを始めますか?
framer.jsonのidを新しい 16 進数値に変更してください。idが重複すると、Framer は 2 つのプラグインを同じプラグインとして扱います。インストール状態、localStorageのオリジン、pluginDataの名前空間が共有されるため、開発環境で気付かないうちにデータが混在します。スキャフォルダーは一意のidを生成するため、リポジトリをコピーする際はそのプロパティを維持してください。
framer.json
すべてのプラグインでは、プロジェクトのルートに framer.json が必要です。
{
"id": "6bbb4f",
"name": "My Plugin",
"modes": ["configureManagedCollection", "syncManagedCollection"],
"icon": "/icon.svg"
}
id— 一意の 16 進数識別子(スキャフォールディングで自動生成されます)modes— サポートするモードの配列(下記参照)icon—public/内に配置する 30×30 の SVG またはビットマップ(ビットマップは 90×90 px で用意してください)。SVG は慎重に中央揃えする必要があります。
プラグインモード
| モード | 用途 | framer.mode の値 |
|---|---|---|
canvas | 汎用のキャンバスアクセス | "canvas" |
configureManagedCollection | CMS:初回セットアップ/フィールド設定 | "configureManagedCollection" |
syncManagedCollection | CMS:既存コレクションの再同期 | "syncManagedCollection" |
image | ユーザーが画像を選択 | "image" |
editImage | 既存の画像を編集 | "editImage" |
collection | ユーザーが編集可能なコレクションにアクセス | "collection" |
code | 現在開いているコードファイルを編集 | "code" |
localization | アクティブなロケールを編集 | "localization" |
CMS プラグインでは、configureManagedCollection と syncManagedCollection の両方を使用します。(v4 の d.ts Mode ユニオンには、ドキュメントに記載されていない api という値も含まれています。ドキュメントに追加されるまでは無視してください。)
Framer コア API
UI 管理
framer.showUI({ position?, width, height, minWidth?, minHeight?, maxWidth?, resizable? })
framer.hideUI()
framer.closePlugin(message?, { variant: "success" | "error" | "info" }) // returns never
framer.notify(message, { variant?, durationMs?, button?: { text, onClick } })
framer.setCloseWarning(message | false) // warn before closing during sync
framer.setBackgroundMessage(message) // status while plugin runs hidden
framer.setMenu([{ label, onAction, visible? }, { type: "separator" }])
framer.showContextMenu(items, { location }) // context menu inside the plugin UI (3.0)
framer.navigateTo(nodeId, { select?, zoom? }) // select + zoom a node into view (3.0; not gated)
framer.subscribeToOpenCodeFile(callback) // fires on code-file open/switch (3.0)
closePluginは内部でFramerPluginClosedErrorをスローします。catch ブロックでは常に無視してください。- ちらつきを避けるため、
showUIはuseLayoutEffect内で呼び出してください。
プロパティ
framer.mode— 現在のモード文字列
コレクションへのアクセス
framer.getActiveManagedCollection() // → Promise<ManagedCollection>
framer.getActiveCollection() // → Promise<Collection> (unmanaged)
framer.getManagedCollections() // → Promise<ManagedCollection[]>
framer.getCollections() // → Promise<Collection[]>
framer.createManagedCollection() // → Promise<ManagedCollection>
キャンバスメソッド(canvas モード)
framer.addImage(image) // NamedImageAssetInput | File → Promise<void>
framer.setImage(image) // sets on the current selection
framer.getImage()
framer.addText(text, options?)
framer.addSVG(svg) // SVGData; max 10kB
framer.addComponentInstance({ url, attributes?, parentId? }) // → Promise<ComponentInstanceNode>
framer.addDetachedComponentLayers({ url, layout, attributes? }) // → Promise<FrameNode> (inlines the layers)
framer.getSelection()
framer.subscribeToSelection(callback)
addComponentInstance は作成されたノードを返すため、保持しておいてください。選択状態を考慮した置換/ジオメトリのコピー処理では、その ID が必要です。addImage/addSVG/addText は void を返します。
CMS/ManagedCollection(概要)
CMS プラグインは独自の領域です。全体像(ManagedCollection API、フィールド型、サイレント同期アルゴリズム、ユーザー編集可能なフィールド、スラッグ生成、フィールドデータ型ラッパー、CMS の注意点)は、cms-managed-collections.md に記載されています。外部データソースを Framer コレクションに同期するプラグインを作成する場合は読んでください。それ以外の場合は読み飛ばしてかまいません。
そのファイルを開かない場合でも、次の 2 点を覚えておいてください。
ManagedCollection.addItems()はidで照合するアップサートです。また、getItems()はなく、getItemIds()のみです。- すべての
fieldData値には、明示的な型ラッパーが必要です:{ type: "string", value: "..." }。生の値は通知なく無視されます。
権限
import { framer, useIsAllowedTo, type ProtectedMethod } from "@framer/plugin"
// Imperative check
framer.isAllowedTo("ManagedCollection.addItems", "ManagedCollection.removeItems")
// React hook (reactive)
const canSync = useIsAllowedTo("ManagedCollection.addItems", "ManagedCollection.removeItems")
// Standard CMS sync permissions
const SYNC_METHODS = [
"ManagedCollection.setFields",
"ManagedCollection.addItems",
"ManagedCollection.removeItems",
"ManagedCollection.setPluginData",
] as const satisfies ProtectedMethod[]
権限による制限の対象となるのは、変更を行うメソッドのみです。 読み取り処理の getItemIds、getPluginData、getActiveManagedCollection、getFields は、SDK の ProtectedMethod テーブルで配列が空になっているため、これらに対する isAllowedTo() は常に true を返します。書き込み処理には権限チェックを行い、読み取り処理には何もしない権限チェックを追加するのではなく、堅牢性を高めるために try/catch で囲んでください。(ドキュメントの表現では、保護されていないメソッドは「ほとんどが get で始まりますが、すべてではありません」。不明な場合は TypeScript の判断に従ってください。名前が ProtectedMethod として受け付けられない場合、権限チェックは不要です。)
マーケットプレイス審査の要件(2026-07 の判定テストで確認): 自動審査では、保護対象のすべての呼び出し箇所に次の 3 点を求めます。呼び出し箇所の近くで明示的に framer.isAllowedTo() を確認すること(かなり上流にあるリアクティブな useIsAllowedTo による早期リターンだけでは不十分です)、ユーザーに明確な失敗メッセージを示す try/catch で呼び出しを囲むこと(何も通知しない処理は不可)、権限がない間は useIsAllowedTo を使って UI を無効にすることです。使用されていないコードや保留中のコードにある、権限チェックのない保護対象の呼び出しも指摘されるため、削除してください。詳細:marketplace.md。
データストレージの判断ツリー
| 必要なもの | 使用するもの | 理由 |
|---|---|---|
| API キー、認証トークン | localStorage | ユーザーごとに保存され、サイズに関する警告がなく、共有されません |
| ユーザー設定 | localStorage | ユーザーごとに保存され、同期的にアクセスできます |
| データソース ID、最終同期時刻 | collection.setPluginData() | コラボレーター間で共有され、コレクションに紐付きます |
| プロジェクトレベルの設定 | framer.setPluginData() | 共有されますが、合計 4kB の上限があります |
| 大きな状態や増加する状態(アイテムごとの同期マップ、キャッシュ) | localStorage | マップの増加に伴い pluginData の 2kB/エントリーの上限に達しますが、localStorage には約数 MB 保存できます |
| ライセンス/利用資格の状態 | framer.getCurrentUser().id をキーとしてバックエンドに保存 | ユーザー ID(64 文字の 16 進数)は、プロジェクトやデバイスをまたいでアカウントごとに安定しています。漏洩、共有、プロジェクトのリミックスへの引き継ぎにつながるクライアント側の情報はありません。有料プラグインに最も適した方法です。詳しくは pitfalls.md をご覧ください |
pluginData:エントリーごとに 2kB、合計 4kB です。文字列のみ使用できます。削除するにはnullを渡します。pluginDataは、小さく上限が決まった値に使用します。 コンテンツ数に応じて増加するもの(アイテムごとの差分同期状態、キャッシュ)は、エントリーごとの 2kB の上限を超えます。localStorageに保存し、共有される正規データではなく、再構築可能な最適化として扱ってください(ユーザーごとの保存であるため、コラボレーターは初回使用時に再構築します)。pluginDataはプラグインごとに名前空間が分かれています。他のプラグインや外部のエージェントツールは、キーを読み書きできません。localStorage:プラグインごとにオリジンがサンドボックス化され、すべてのプロジェクト間で保持されますが、ブラウザー/デバイスごとです。あくまで緩やかな制約であり、強制力はありません。setPluginData()(およびその他の保護されたメソッド)は、事前にframer.isAllowedTo()を確認せずに呼び出すと、「保護されたメッセージタイプを呼び出しています」というトーストを表示します。これは権限システムの動作であり、バグではありません。呼び出しを権限チェックの後に行えば、トーストは表示されなくなります。
ライセンス対象のプラグインとプロジェクトのリミックス
pluginData はプロジェクトがリミックスされる際に、そのままコピーされます。ライセンスキーと認証情報も新しいプロジェクトに引き継がれ、有効に見えてしまいます。リミックスを検出し、読み込み時に古い認証情報を消去してください。
const { id: currentProjectId } = await framer.getProjectInfo()
if (storedProjectId !== currentProjectId) {
// Remixed → wipe credentials so the new owner does a fresh activation
if (framer.isAllowedTo("setPluginData")) {
await framer.setPluginData(PD_LICENSE_KEY, null)
// ... clear any other project-bound keys
}
setLicenseActive(false)
}
有効化時にプロジェクト ID を保存し、読み込みのたびに比較します。isAllowedTo が false の場合は、消去を行わずに処理を続けてください(次回の読み込み時に再試行します)。詳しいパターンとプロバイダーに関する注意事項は、pitfalls.md をご覧ください。
コードファイルとホスト型モジュール(canvas モード)
本番環境の canvas プラグインで @framer/plugin v4 を検証済みです(2026-06)。
framer.createCodeFile(name, code, { editViaPlugin?: boolean })
// editViaPlugin: true → the file's "Edit Code" action opens this plugin instead of the editor
framer.getCodeFiles() // → Promise<CodeFile[]>
codeFile.setFileContent(code) // new version; UPDATES already-inserted canvas instances in place
codeFile.typecheck() // TS diagnostics without leaving the plugin
codeFile.exports // [{ name, componentId, insertURL, type }]
framer.addComponentInstance({ url }) // accepts insertURL or any module URL
instance.setAttributes({ controls }) // rewrites property-control values in place, repeatable
- canvas インスタンスの照合には
insertURLではなく、必ずcomponentIdentifierを使ってください。 識別子は再生成後も安定しており、local-module:codeFile/{fileId}:{exportName}です。insertURLはバージョンに固定され(…/Name-hash.js@{versionId})、setFileContentのたびに変わります。 - コードファイルは、相対パス(
./Engine.tsx)または絶対モジュール URLで相互にインポートできます。ビルドパイプラインは URL インポートをそのまま保持します。 - すべてのコードファイルは
https://framer.com/m/{Name}-{hash}.js[@version]で公開ホストされます。非公開プロジェクトからでも認証は不要です。 バージョン指定のない URL は保存するとすぐに最新の状態を参照するため、公開手順は不要です。バージョン固定 URL は不変で、ファイルを削除しても利用できます。 - 別のプロジェクトのモジュール URL を指定した
addComponentInstance({ url })は機能します。識別子はmodule:{moduleId}/{version}/{Name}.js:{export}になります(module:{moduleId}/プレフィックスで照合してください)。利用側のプロジェクトにコードファイルは作成されません。 ソースの更新は、インスタンスに手動の「Update」ボタンとして表示されます。 - コンポーネントの上にコメント注釈として記述する
@framerDisableUnlinkは、共有/ホスト型コンポーネントの「Edit Code」やリンク解除を無効にします。ローカルのコードファイルを非表示にすることはできません。ローカルのファイルは常に表示され、「Assets」>「Code」で編集できます。 - コードファイルのインポート(2026-07 検証済み): 裸の npm 指定子で解決できるのは
react、react-dom、framer、framer-motionのみです。バージョン付き指定子(react@18)は拒否されます。それ以外はすべて URL インポートで取り込みます。バージョンを固定してください(例:https://esm.sh/<pkg>@<version>?external=react,react-dom。react を外部化することで、Framer のランタイムが独自のものを提供します)。コンパイル済みモジュールでは URL がそのまま保持されるため、バージョン固定の insertURL を使うと依存関係ツリーが永久に固定されます。サイレントに失敗している場合の兆候: 型チェックが通る一方でコードファイルのexportsが[]のままなら、サポートされていないインポートがあります。ビルドはエラー表示なしで失敗します。 - プロジェクトをまたぐ挿入では
@framerIntrinsicの幅/高さを信用しないでください(2026-07 検証済み):addComponentInstance({ url })は、コンパイル済みのメタデータが同一でも、一部のホスト型モジュールでしかこの注釈を反映しません。挿入するサイズが重要な場合は、挿入後に明示的に設定してください:node.setAttributes({ width: "600px", height: "400px" })。
"@framer/plugin" からの主なエクスポート
import { framer, useIsAllowedTo, FramerPluginClosedError } from "@framer/plugin"
import type {
ManagedCollection, ManagedCollectionField, ManagedCollectionFieldInput,
ManagedCollectionItemInput, FieldDataInput, FieldDataEntryInput,
ProtectedMethod, Collection, CollectionItem
} from "@framer/plugin"
import "@framer/plugin/framer.css"
参考資料
詳しくは、このスキルディレクトリにある関連ファイルを参照してください。
- api-reference.md — API シグネチャ、ノードのジオメトリ、権限メソッド、型定義の完全な一覧
- cms-managed-collections.md — CMS プラグイン:ManagedCollection API、同期アルゴリズム、フィールドタイプ、スラッグ、CMS の注意点(CMS 以外のプラグインでは不要)
- patterns.md — 複数の領域に共通するパターン:認証(OAuth/API キー)、UI のサイズ設定、進捗状況、耐障害性、メニュー、canvas への挿入
- pitfalls.md — 既知の注意点、プロジェクトのリミックス/ライセンスに関するパターン、デバッグのヒント
- marketplace.md — 提出ワークフロー、掲載仕様、審査プロセス、ポリシー、公開後の義務
主なルール
常に
- プロジェクト固有の上書き設定や決定事項について、プロジェクトの
CLAUDE.mdを確認してください。 - 新機能を実装する前に、必ず marketplace.md を確認してください。現在の公式ドキュメントによると、プラグインは審査なしですぐに公開され、品質基準(英語の UI、ライトモードとダークモード、米ドル価格、広告なし、知的財産ルール)は推奨事項として示されています。しかし、自動化された公開後のコードスキャンにより、セキュリティと権限に関する要件は実際に数時間以内に検証され、公開中の掲載が取り下げられる場合があります(2026 年 7 月の判定テストで確認)。推奨事項も必須要件として扱って実装してください。
- ネイティブのスタイルを適用するには
"@framer/plugin/framer.css"をインポートしてください。 - Framer の CSS による上書きを避けるため、
<button>の代わりに<div role="button">を使用してください。 FramerPluginClosedErrorは catch ブロックで処理し、何も表示せずに無視してください。- リサイズ時のちらつきを防ぐため、
useLayoutEffectでshowUIを呼び出してください。 - 保護対象(変更を伴う)の呼び出しごとに、
framer.isAllowedTo()(同期処理で、booleanを返します)を隣接して確認し、ユーザーに表示する失敗時のメッセージを指定して、その呼び出しを try/catch で囲み、useIsAllowedToを使って操作を開始する UI を無効にしてください。マーケットプレイスの審査担当者は、各呼び出し箇所でこの 3 点すべてを確認します。読み取り操作に制限はないため、何もしないガードを追加しないでください。zip には元のソースとソースマップを含めてください(marketplace.md を参照)。 - ユーザーごとのシークレットには
localStorageを、共有状態にはpluginDataを使用してください。また、プロジェクトのリミックス時に不一致があれば、認証情報を消去してください(上記のライセンス付きプラグインを参照)。
キャンバス/コードファイルのプラグイン
9. キャンバス上のインスタンスは、insertURL(バージョンに固定されており、ファイルの保存ごとに変わります)ではなく、必ず componentIdentifier で照合してください。
10. 生成または管理するコードファイルには { editViaPlugin: true } を渡し、「Edit Code」からユーザーがプラグインに戻るようにしてください。
11. UI 内にタイトルバーを表示しないでください。Framer のウィンドウ枠にプラグイン名と閉じるボタンがすでに表示されるため、区切りには border-top の 1px の線だけで十分です。
12. プラグインのリポジトリを複製する場合は、framer.json の id を新しい値にしてください(ID が重複すると状態が共有されます)。
CMS プラグイン — 詳細は cms-managed-collections.md を参照してください。
13. UI を表示する前に、syncManagedCollection モードでサイレント同期を試みてください。
14. addItems() はアップサートです。fieldData の値には明示的に { type, value } が必要です。全件削除してから再追加する方法は使わないでください。また、fieldData に userEditable フィールドを含めないでください。