🛠️ FosmvvmReactViewジェネレーター
FOSMVVMという設計思想に基づき、画面表示
📺 まず動画で見る(YouTube)
▶ 【衝撃】最強のAIエージェント「Claude Code」の最新機能・使い方・プログラミングをAIで効率化する超実践術を解説! ↗
※ jpskill.com 編集部が参考用に選んだ動画です。動画の内容と Skill の挙動は厳密には一致しないことがあります。
📜 元の英語説明(参考)
Generate React components that render FOSMVVM ViewModels. Scaffolds ViewModelView pattern with hooks, loading states, and TypeScript types.
🇯🇵 日本人クリエイター向け解説
FOSMVVMという設計思想に基づき、画面表示
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o fosmvvm-react-view-generator.zip https://jpskill.com/download/4830.zip && unzip -o fosmvvm-react-view-generator.zip && rm fosmvvm-react-view-generator.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/4830.zip -OutFile "$d\fosmvvm-react-view-generator.zip"; Expand-Archive "$d\fosmvvm-react-view-generator.zip" -DestinationPath $d -Force; ri "$d\fosmvvm-react-view-generator.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
fosmvvm-react-view-generator.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
fosmvvm-react-view-generatorフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-17
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
💬 こう話しかけるだけ — サンプルプロンプト
- › Fosmvvm React View Generator を使って、最小構成のサンプルコードを示して
- › Fosmvvm React View Generator の主な使い方と注意点を教えて
- › Fosmvvm React View Generator を既存プロジェクトに組み込む方法を教えて
これをClaude Code に貼るだけで、このSkillが自動発動します。
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
[スキル名] fosmvvm-react-view-generator
FOSMVVM Reactビュージェネレーター
FOSMVVM ViewModelをレンダリングするReactコンポーネントを生成します。
概念的基盤
完全なアーキテクチャのコンテキストについては、FOSMVVMArchitecture.md | OpenClawリファレンスをご覧ください。
FOSMVVMでは、ReactコンポーネントはViewModelを表示する薄いレンダリング層です。
┌─────────────────────────────────────────────────────────────┐
│ ViewModelView Pattern │
├─────────────────────────────────────────────────────────────┤
│ │
│ ViewModel (Data) React Component │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ title: String │────►│ <h1>{vm.title} │ │
│ │ items: [Item] │────►│ {vm.items.map()} │ │
│ │ isEnabled: Bool │────►│ disabled={!...} │ │
│ └──────────────────┘ └──────────────────┘ │
│ │
│ ServerRequest (Actions) │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ processRequest() │◄────│ <Component.bind │ │
│ │ │ │ requestType={} │ │
│ └──────────────────┘ └──────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
主要な原則: コンポーネントはデータを変換したり計算したりしません。ViewModelが提供するものをレンダリングします。
ビューとViewModelの整合性
コンポーネントのファイル名は、それがレンダリングするViewModelと一致させる必要があります。
src/
viewmodels/
{Feature}ViewModel.js ←──┐
{Entity}CardViewModel.js ←──┼── 同じ名前
│
components/ │
{Feature}/ │
{Feature}View.jsx ────┤ ({Feature}ViewModelをレンダリング)
{Entity}CardView.jsx ────┘ ({Entity}CardViewModelをレンダリング)
この整合性により、以下が提供されます。
- 発見しやすさ - 任意のViewModelのコンポーネントを即座に見つけられます。
- 一貫性 - SwiftUIやLeafと同じ命名規則です。
- 保守性 - ViewModelの変更がコンポーネントの場所にも反映されます。
TDDワークフロー
このスキルは、単一の呼び出しでテストを最初に、実装を次に生成します。
1. 会話コンテキストからViewModelとServerRequestの詳細を参照します。
2. .test.jsファイルを生成します → テストは失敗します(まだ実装がないため)。
3. .jsxファイルを生成します → テストは合格します。
4. 完了を確認します(両方のファイルが存在すること)。
5. ユーザーが `npm test` を実行します → すべてのテストが合格します ✓
コンテキスト認識: スキルは要件の会話理解を参照します。ファイル解析やQ&Aは必要ありません。
コアコンポーネント
1. viewModelComponent()ラッパー
すべてのコンポーネントはviewModelComponent()でラップされます。
const MyView = FOSMVVM.viewModelComponent(({ viewModel }) => {
return <div>{viewModel.title}</div>;
});
export default MyView;
必須:
- グローバル名前空間から
FOSMVVM.viewModelComponent()を使用します(スクリプトタグ経由でロードされます)。 - コンポーネント関数は
{ viewModel }プロパティを受け取ります。 - インポートは不要です - FOSMVVMユーティリティは
<script>タグ経由でロードされます。
2. .bind()パターン
親コンポーネントは.bind()を使用してServerRequestを呼び出します。
// 親コンポーネント
function Dashboard() {
return (
<div>
<TaskList.bind({
requestType: 'GetTasksRequest',
params: { status: 'active' }
}) />
</div>
);
}
.bind()パターン:
- 子コンポーネントはServerRequest経由でデータを受け取ります。
- 親は
requestTypeとparamsを指定します。 - WASMブリッジがリクエスト → ViewModel → コンポーネントのレンダリングを処理します。
fetch()呼び出しやハードコードされたURLはありません。
3. エラーViewModelの処理
エラーViewModelは他のViewModelと同様にレンダリングされます。
const TaskCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
// エラーViewModelを処理します
if (viewModel.errorType === 'NotFoundError') {
return (
<div className="error">
<p>{viewModel.message}</p>
<p>{viewModel.suggestedAction}</p>
</div>
);
}
if (viewModel.errorType === 'ValidationError') {
return (
<div className="validation-error">
<h3>{viewModel.title}</h3>
<ul>
{viewModel.errors.map(err => (
<li key={err.field}>{err.message}</li>
))}
</ul>
</div>
);
}
// 成功ViewModelをレンダリングします
return (
<div className="task-card">
<h3>{viewModel.title}</h3>
<p>{viewModel.description}</p>
</div>
);
});
主要な原則:
- 汎用的なエラー処理はありません。
- 各エラータイプには独自のViewModelがあります。
- コンポーネントは
errorTypeプロパティに基づいて条件付きでレンダリングします。 - エラーレンダリングは単なるデータレンダリングです。
4. ナビゲーションインテント(URLではない)
ハードコードされたパスではなく、ナビゲーションインテントを使用します。
// FOSMVVMユーティリティは<script>タグ経由でロードされ、グローバル名前空間で利用可能です
// ❌ 絶対にしない
<a href="/tasks/123">View Task</a>
// ✅ 常に
<FOSMVVM.Link to={{ intent: 'viewTask', id: viewModel.id }}>
{viewModel.linkText}
</FOSMVVM.Link>
ナビゲーションパターン:
- グローバル名前空間から
FOSMVVM.Linkを使用します(スクリプトタグ経由でロードされます)。 - ハードコードされたパスではなく、
intentプロパティを使用します。 - ルーターがインテントをルートにマッピングします。
- プラットフォームに依存しないナビゲーションです。
コンポーネントカテゴリ
表示のみのコンポーネント
データをレンダリングするだけのコンポーネント(ユーザーインタラクションなし)。
const InfoCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
return (
<div className="info-card">
<h2>{viewModel.title}</h2>
<p>{viewModel.description}</p>
{viewModel.isActive && (
<span className="badge">{viewModel.activeLabel}</span>
)}
</div>
);
});
export default InfoCard;
特徴:
- ViewModelのプロパティのみをレンダリングします。
- イベントハンドラ(onClick、onSubmitなど)はありません。
- 持つ場合があります。
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
FOSMVVM React View Generator
Generate React components that render FOSMVVM ViewModels.
Conceptual Foundation
For full architecture context, see FOSMVVMArchitecture.md | OpenClaw reference
In FOSMVVM, React components are thin rendering layers that display ViewModels:
┌─────────────────────────────────────────────────────────────┐
│ ViewModelView Pattern │
├─────────────────────────────────────────────────────────────┤
│ │
│ ViewModel (Data) React Component │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ title: String │────►│ <h1>{vm.title} │ │
│ │ items: [Item] │────►│ {vm.items.map()} │ │
│ │ isEnabled: Bool │────►│ disabled={!...} │ │
│ └──────────────────┘ └──────────────────┘ │
│ │
│ ServerRequest (Actions) │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ processRequest() │◄────│ <Component.bind │ │
│ │ │ │ requestType={} │ │
│ └──────────────────┘ └──────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Key principle: Components don't transform or compute data. They render what the ViewModel provides.
View-ViewModel Alignment
The component filename should match the ViewModel it renders.
src/
viewmodels/
{Feature}ViewModel.js ←──┐
{Entity}CardViewModel.js ←──┼── Same names
│
components/ │
{Feature}/ │
{Feature}View.jsx ────┤ (renders {Feature}ViewModel)
{Entity}CardView.jsx ────┘ (renders {Entity}CardViewModel)
This alignment provides:
- Discoverability - Find the component for any ViewModel instantly
- Consistency - Same naming discipline as SwiftUI and Leaf
- Maintainability - Changes to ViewModel are reflected in component location
TDD Workflow
This skill generates tests FIRST, implementation SECOND in a single invocation:
1. Reference ViewModel and ServerRequest details from conversation context
2. Generate .test.js file → Tests FAIL (no implementation yet)
3. Generate .jsx file → Tests PASS
4. Verify completeness (both files exist)
5. User runs `npm test` → All tests pass ✓
Context-aware: Skill references conversation understanding of requirements. No file parsing or Q&A needed.
Core Components
1. viewModelComponent() Wrapper
Every component is wrapped with viewModelComponent():
const MyView = FOSMVVM.viewModelComponent(({ viewModel }) => {
return <div>{viewModel.title}</div>;
});
export default MyView;
Required:
- Use
FOSMVVM.viewModelComponent()from global namespace (loaded via script tag) - Component function receives
{ viewModel }prop - No imports needed - FOSMVVM utilities loaded via
<script>tags
2. The .bind() Pattern
Parent components use .bind() to invoke ServerRequests:
// Parent component
function Dashboard() {
return (
<div>
<TaskList.bind({
requestType: 'GetTasksRequest',
params: { status: 'active' }
}) />
</div>
);
}
The .bind() pattern:
- Child components receive data via ServerRequest
- Parent specifies
requestTypeandparams - WASM bridge handles request → ViewModel → component rendering
- No fetch() calls, no hardcoded URLs
3. Error ViewModel Handling
Error ViewModels are rendered like any other ViewModel:
const TaskCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
// Handle error ViewModels
if (viewModel.errorType === 'NotFoundError') {
return (
<div className="error">
<p>{viewModel.message}</p>
<p>{viewModel.suggestedAction}</p>
</div>
);
}
if (viewModel.errorType === 'ValidationError') {
return (
<div className="validation-error">
<h3>{viewModel.title}</h3>
<ul>
{viewModel.errors.map(err => (
<li key={err.field}>{err.message}</li>
))}
</ul>
</div>
);
}
// Render success ViewModel
return (
<div className="task-card">
<h3>{viewModel.title}</h3>
<p>{viewModel.description}</p>
</div>
);
});
Key principles:
- No generic error handling
- Each error type has its own ViewModel
- Component conditionally renders based on
errorTypeproperty - Error rendering is just data rendering
4. Navigation Intents (Not URLs)
Use navigation intents, not hardcoded paths:
// FOSMVVM utilities loaded via <script> tag, available on global namespace
// ❌ NEVER
<a href="/tasks/123">View Task</a>
// ✅ ALWAYS
<FOSMVVM.Link to={{ intent: 'viewTask', id: viewModel.id }}>
{viewModel.linkText}
</FOSMVVM.Link>
Navigation patterns:
- Use
FOSMVVM.Linkfrom global namespace (loaded via script tag) - Use
intentproperty, not hardcoded paths - Router maps intents to routes
- Platform-independent navigation
Component Categories
Display-Only Components
Components that just render data (no user interactions):
const InfoCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
return (
<div className="info-card">
<h2>{viewModel.title}</h2>
<p>{viewModel.description}</p>
{viewModel.isActive && (
<span className="badge">{viewModel.activeLabel}</span>
)}
</div>
);
});
export default InfoCard;
Characteristics:
- Just renders ViewModel properties
- No event handlers (onClick, onSubmit, etc.)
- May have conditional rendering based on ViewModel state
- No .bind() calls to child components
Interactive Components
Components with user actions that trigger ServerRequests:
const ActionCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
return (
<div className="action-card">
<h2>{viewModel.title}</h2>
<p>{viewModel.description}</p>
<div className="actions">
<button
onClick={() => viewModel.operations.performAction()}
disabled={!viewModel.canPerformAction}
>
{viewModel.actionLabel}
</button>
<button onClick={() => viewModel.operations.cancel()}>
{viewModel.cancelLabel}
</button>
</div>
</div>
);
});
export default ActionCard;
List Components
Components that render collections:
const TaskList = FOSMVVM.viewModelComponent(({ viewModel }) => {
if (viewModel.isEmpty) {
return <div className="empty">{viewModel.emptyMessage}</div>;
}
return (
<div className="task-list">
<h2>{viewModel.title}</h2>
<p>{viewModel.totalCount}</p>
{viewModel.tasks.map(task => (
<TaskCard.bind({
requestType: 'GetTaskRequest',
params: { id: task.id }
}) />
))}
</div>
);
});
export default TaskList;
Form Components
Components with validated input fields:
const SignInForm = FOSMVVM.viewModelComponent(({ viewModel }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [errors, setErrors] = useState({});
const handleSubmit = async (e) => {
e.preventDefault();
const result = await viewModel.operations.submit({
email,
password
});
if (result.validationErrors) {
setErrors(result.validationErrors);
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>{viewModel.emailLabel}</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder={viewModel.emailPlaceholder}
/>
{errors.email && <span className="error">{errors.email}</span>}
</div>
<div>
<label>{viewModel.passwordLabel}</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder={viewModel.passwordPlaceholder}
/>
{errors.password && <span className="error">{errors.password}</span>}
</div>
<button type="submit" disabled={viewModel.submitDisabled}>
{viewModel.submitLabel}
</button>
</form>
);
});
export default SignInForm;
When to Use This Skill
- Creating a new React component for a FOSMVVM app
- Building UI to render a ViewModel
- Migrating Leaf templates to React
- Following an implementation plan that requires new views
- Creating forms with validation
- Building list views that compose child components
What This Skill Generates
Two files per invocation:
| File | Location | Purpose |
|---|---|---|
{ViewName}View.test.js |
src/components/{Feature}/ |
Jest + React Testing Library tests |
{ViewName}View.jsx |
src/components/{Feature}/ |
React component |
Test file generated FIRST (tests fail initially) Implementation file generated SECOND (tests pass)
Note: The corresponding ViewModel and ServerRequest should already exist (use other FOSMVVM generator skills).
Project Structure Configuration
| Placeholder | Description | Example |
|---|---|---|
{ViewName} |
View name (without "View" suffix) | TaskList, SignIn |
{Feature} |
Feature/module grouping | Tasks, Auth |
Pattern Implementation
This skill references conversation context to determine component structure:
Component Type Detection
From conversation context, the skill identifies:
- ViewModel structure (from prior discussion or specifications read by Claude)
- ServerRequest details (from requirements already in context)
- Component category: Display-only, interactive, form, or list
- Error ViewModels to handle
Test Generation (FIRST)
Based on component type, generates .test.js with:
- All components: Success ViewModel rendering, error ViewModel rendering
- Interactive: Button clicks, operation verification
- Form: Input changes, validation errors, submission
- List: Empty state, multiple items, child binding
Component Generation (SECOND)
Generates .jsx following patterns:
- Import
viewModelComponentwrapper - Handle error ViewModels with conditional rendering
- Render success ViewModel
- Add interactions (if interactive)
- Add form state (if form)
- Add child
.bind()calls (if container) - Export wrapped component
Context Sources
Skill references information from:
- Prior conversation: Requirements discussed with user
- Specification files: If Claude has read specifications into context
- ViewModel definitions: From codebase or discussion
Step 5: Verify Completeness
Check:
- [ ]
.test.jsfile exists - [ ]
.jsxfile exists - [ ] Component uses
FOSMVVM.viewModelComponent()wrapper - [ ] Component accesses FOSMVVM functions from global namespace
- [ ] Tests cover success and error ViewModels
- [ ] Tests cover user interactions (if applicable)
Key Patterns
Pattern: No Business Logic in Components
// ❌ BAD - Component is transforming data
const TaskCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
const daysLeft = Math.ceil((viewModel.dueDate - Date.now()) / 86400000);
return <span>{daysLeft} days remaining</span>;
});
// ✅ GOOD - ViewModel provides shaped result
const TaskCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
return <span>{viewModel.daysRemainingText}</span>;
});
Pattern: No fetch() Calls
// ❌ BAD - Component making HTTP requests
const TaskCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
const [data, setData] = useState(null);
useEffect(() => {
fetch(`/api/tasks/${viewModel.id}`)
.then(r => r.json())
.then(setData);
}, [viewModel.id]);
return <div>{data?.title}</div>;
});
// ✅ GOOD - Parent uses .bind() to invoke ServerRequest
<TaskCard.bind({
requestType: 'GetTaskRequest',
params: { id: taskId }
}) />
Pattern: Error ViewModels Are Data
// ❌ BAD - Generic error handling
const TaskCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
if (viewModel.error) {
return <div>Error: {viewModel.error.message}</div>;
}
return <div>{viewModel.title}</div>;
});
// ✅ GOOD - Specific error ViewModels
const TaskCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
if (viewModel.errorType === 'NotFoundError') {
return (
<div className="not-found">
<h3>{viewModel.errorTitle}</h3>
<p>{viewModel.errorMessage}</p>
<p>{viewModel.suggestedAction}</p>
</div>
);
}
if (viewModel.errorType === 'ValidationError') {
return (
<div className="validation-error">
<h3>{viewModel.errorTitle}</h3>
<ul>
{viewModel.validationErrors.map(err => (
<li key={err.field}>{err.message}</li>
))}
</ul>
</div>
);
}
return <div>{viewModel.title}</div>;
});
Pattern: Navigation Intents
// ❌ BAD - Hardcoded URLs
const TaskRow = FOSMVVM.viewModelComponent(({ viewModel }) => {
return (
<div>
<a href={`/tasks/${viewModel.id}`}>{viewModel.title}</a>
</div>
);
});
// ✅ GOOD - Navigation intents
// FOSMVVM utilities loaded via <script> tag, available on global namespace
const TaskRow = FOSMVVM.viewModelComponent(({ viewModel }) => {
return (
<div>
<FOSMVVM.Link to={{ intent: 'viewTask', id: viewModel.id }}>
{viewModel.title}
</FOSMVVM.Link>
</div>
);
});
File Organization
src/components/
├── {Feature}/
│ ├── {Feature}View.jsx # Full page → {Feature}ViewModel
│ ├── {Feature}View.test.js # Tests for {Feature}View
│ ├── {Entity}CardView.jsx # Child component → {Entity}CardViewModel
│ ├── {Entity}CardView.test.js # Tests for {Entity}CardView
│ └── {Entity}RowView.jsx # Child component → {Entity}RowViewModel
├── Shared/
│ ├── HeaderView.jsx # Shared components
│ └── FooterView.jsx
Common Mistakes
Computing Data in Components
// ❌ BAD - Component is transforming data
const UserCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
return <div>{viewModel.firstName} {viewModel.lastName}</div>;
});
// ✅ GOOD - ViewModel provides shaped result
const UserCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
return <div>{viewModel.fullName}</div>;
});
Making HTTP Requests Directly
// ❌ BAD - fetch() call in component
const TaskList = FOSMVVM.viewModelComponent(({ viewModel }) => {
const [tasks, setTasks] = useState([]);
useEffect(() => {
fetch('/api/tasks').then(r => r.json()).then(setTasks);
}, []);
return <div>{tasks.map(t => <div key={t.id}>{t.title}</div>)}</div>;
});
// ✅ GOOD - Parent uses .bind() with ServerRequest
<TaskList.bind({
requestType: 'GetTasksRequest',
params: {}
}) />
Hardcoding Text
// ❌ BAD - Not localizable
const TaskCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
return (
<button onClick={viewModel.operations.submit}>
Submit
</button>
);
});
// ✅ GOOD - ViewModel provides localized text
const TaskCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
return (
<button onClick={viewModel.operations.submit}>
{viewModel.submitLabel}
</button>
);
});
Using Hardcoded URLs
// ❌ BAD - Hardcoded path
const TaskRow = FOSMVVM.viewModelComponent(({ viewModel }) => {
return <a href={`/tasks/${viewModel.id}`}>{viewModel.title}</a>;
});
// ✅ GOOD - Navigation intent
// FOSMVVM utilities loaded via <script> tag, available on global namespace
const TaskRow = FOSMVVM.viewModelComponent(({ viewModel }) => {
return (
<FOSMVVM.Link to={{ intent: 'viewTask', id: viewModel.id }}>
{viewModel.title}
</FOSMVVM.Link>
);
});
Not Wrapping with viewModelComponent()
// ❌ BAD - Missing viewModelComponent() wrapper
const TaskCard = ({ viewModel }) => {
return <div>{viewModel.title}</div>;
};
export default TaskCard;
// ✅ GOOD - Wrapped with viewModelComponent()
const TaskCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
return <div>{viewModel.title}</div>;
});
export default TaskCard;
Mismatched Filenames
// ❌ BAD - Filename doesn't match ViewModel
ViewModel: TaskListViewModel
Component: Tasks.jsx
// ✅ GOOD - Aligned names
ViewModel: TaskListViewModel
Component: TaskListView.jsx
File Templates
See reference.md for complete file templates.
Naming Conventions
| Concept | Convention | Example |
|---|---|---|
| Component file | {Name}View.jsx |
TaskListView.jsx, SignInView.jsx |
| Test file | {Name}View.test.js |
TaskListView.test.js |
| Component function | {Name}View |
TaskListView, SignInView |
| ViewModel prop | viewModel |
Always viewModel |
Testing Patterns
Test: Rendering with Success ViewModel
it('renders task card with ViewModel', () => {
const viewModel = {
title: 'Test Task',
description: 'Test Description',
dueDate: 'Jan 30, 2026'
};
render(<TaskCard viewModel={viewModel} />);
expect(screen.getByText('Test Task')).toBeInTheDocument();
expect(screen.getByText('Test Description')).toBeInTheDocument();
});
Test: Rendering with Error ViewModel
it('renders NotFoundViewModel', () => {
const viewModel = {
errorType: 'NotFoundError',
errorTitle: 'Task Not Found',
errorMessage: 'The task you requested does not exist',
suggestedAction: 'Try searching for a different task'
};
render(<TaskCard viewModel={viewModel} />);
expect(screen.getByText('Task Not Found')).toBeInTheDocument();
expect(screen.getByText(/does not exist/)).toBeInTheDocument();
});
Test: User Interaction
it('calls operation when button clicked', () => {
const mockOperation = jest.fn();
const viewModel = {
title: 'Test Task',
submitLabel: 'Complete Task',
operations: {
complete: mockOperation
}
};
render(<TaskCard viewModel={viewModel} />);
fireEvent.click(screen.getByText('Complete Task'));
expect(mockOperation).toHaveBeenCalled();
});
How to Use This Skill
Invocation:
/fosmvvm-react-view-generator
Prerequisites:
- ViewModel and ServerRequest details are understood from conversation
- Optionally, specification files have been read into context
- Component requirements (display-only, interactive, form, list) are clear from discussion
Output:
{ComponentName}.test.js- Generated FIRST (tests fail){ComponentName}.jsx- Generated SECOND (tests pass)
Workflow integration: This skill is typically used after discussing requirements or reading specification files. The skill references that context automatically—no file paths or Q&A needed.
See Also
- Architecture Patterns - Mental models and patterns
- FOSMVVMArchitecture.md - Full FOSMVVM architecture
- fosmvvm-swiftui-view-generator - SwiftUI equivalent
- fosmvvm-leaf-view-generator - Leaf equivalent
- reference.md - Complete file templates
Version History
| Version | Date | Changes |
|---|---|---|
| 1.0 | 2026-01-23 | Initial skill for React view generation based on Kairos requirements |