jpskill.com
🛠️ 開発・MCP コミュニティ 🔴 エンジニア向け 👤 エンジニア・AI開発者

🛠️ FosmvvmReactViewジェネレーター

fosmvvm-react-view-generator

FOSMVVMという設計思想に基づき、画面表示

⏱ ボイラープレート実装 半日 → 30分

📺 まず動画で見る(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本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
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
🪟 Windows (PowerShell)
$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. 1. 下の青いボタンを押して fosmvvm-react-view-generator.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → fosmvvm-react-view-generator フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

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経由でデータを受け取ります。
  • 親はrequestTypeparamsを指定します。
  • 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 requestType and params
  • 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 errorType property
  • 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.Link from global namespace (loaded via script tag)
  • Use intent property, 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:

  1. Import viewModelComponent wrapper
  2. Handle error ViewModels with conditional rendering
  3. Render success ViewModel
  4. Add interactions (if interactive)
  5. Add form state (if form)
  6. Add child .bind() calls (if container)
  7. 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.js file exists
  • [ ] .jsx file 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


Version History

Version Date Changes
1.0 2026-01-23 Initial skill for React view generation based on Kairos requirements