← Back to Skills Marketplace
foscomputerservices

FOSMVVM React View Generator

by David Hunt · GitHub ↗ · v2.0.6
darwinlinux ⚠ pending
626
Downloads
2
Stars
0
Active Installs
1
Versions
Install in OpenClaw
/install fosmvvm-react-view-generator
Description
Generate React components rendering FOSMVVM ViewModels with hooks, loading states, TypeScript types, test-first scaffolding, and .bind() server request integ...
README (SKILL.md)

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    │────►│ \x3Ch1>{vm.title}   │             │
│  │ items: [Item]    │────►│ {vm.items.map()} │             │
│  │ isEnabled: Bool  │────►│ disabled={!...}  │             │
│  └──────────────────┘     └──────────────────┘             │
│                                                              │
│  ServerRequest (Actions)                                     │
│  ┌──────────────────┐     ┌──────────────────┐             │
│  │ processRequest() │◄────│ \x3CComponent.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 \x3Cdiv>{viewModel.title}\x3C/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 \x3Cscript> tags

2. The .bind() Pattern

Parent components use .bind() to invoke ServerRequests:

// Parent component
function Dashboard() {
  return (
    \x3Cdiv>
      \x3CTaskList.bind({
        requestType: 'GetTasksRequest',
        params: { status: 'active' }
      }) />
    \x3C/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 (
      \x3Cdiv className="error">
        \x3Cp>{viewModel.message}\x3C/p>
        \x3Cp>{viewModel.suggestedAction}\x3C/p>
      \x3C/div>
    );
  }

  if (viewModel.errorType === 'ValidationError') {
    return (
      \x3Cdiv className="validation-error">
        \x3Ch3>{viewModel.title}\x3C/h3>
        \x3Cul>
          {viewModel.errors.map(err => (
            \x3Cli key={err.field}>{err.message}\x3C/li>
          ))}
        \x3C/ul>
      \x3C/div>
    );
  }

  // Render success ViewModel
  return (
    \x3Cdiv className="task-card">
      \x3Ch3>{viewModel.title}\x3C/h3>
      \x3Cp>{viewModel.description}\x3C/p>
    \x3C/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 \x3Cscript> tag, available on global namespace

// ❌ NEVER
\x3Ca href="/tasks/123">View Task\x3C/a>

// ✅ ALWAYS
\x3CFOSMVVM.Link to={{ intent: 'viewTask', id: viewModel.id }}>
  {viewModel.linkText}
\x3C/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 (
    \x3Cdiv className="info-card">
      \x3Ch2>{viewModel.title}\x3C/h2>
      \x3Cp>{viewModel.description}\x3C/p>

      {viewModel.isActive && (
        \x3Cspan className="badge">{viewModel.activeLabel}\x3C/span>
      )}
    \x3C/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 (
    \x3Cdiv className="action-card">
      \x3Ch2>{viewModel.title}\x3C/h2>
      \x3Cp>{viewModel.description}\x3C/p>

      \x3Cdiv className="actions">
        \x3Cbutton
          onClick={() => viewModel.operations.performAction()}
          disabled={!viewModel.canPerformAction}
        >
          {viewModel.actionLabel}
        \x3C/button>

        \x3Cbutton onClick={() => viewModel.operations.cancel()}>
          {viewModel.cancelLabel}
        \x3C/button>
      \x3C/div>
    \x3C/div>
  );
});

export default ActionCard;

List Components

Components that render collections:

const TaskList = FOSMVVM.viewModelComponent(({ viewModel }) => {
  if (viewModel.isEmpty) {
    return \x3Cdiv className="empty">{viewModel.emptyMessage}\x3C/div>;
  }

  return (
    \x3Cdiv className="task-list">
      \x3Ch2>{viewModel.title}\x3C/h2>
      \x3Cp>{viewModel.totalCount}\x3C/p>

      {viewModel.tasks.map(task => (
        \x3CTaskCard.bind({
          requestType: 'GetTaskRequest',
          params: { id: task.id }
        }) />
      ))}
    \x3C/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 (
    \x3Cform onSubmit={handleSubmit}>
      \x3Cdiv>
        \x3Clabel>{viewModel.emailLabel}\x3C/label>
        \x3Cinput
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          placeholder={viewModel.emailPlaceholder}
        />
        {errors.email && \x3Cspan className="error">{errors.email}\x3C/span>}
      \x3C/div>

      \x3Cdiv>
        \x3Clabel>{viewModel.passwordLabel}\x3C/label>
        \x3Cinput
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          placeholder={viewModel.passwordPlaceholder}
        />
        {errors.password && \x3Cspan className="error">{errors.password}\x3C/span>}
      \x3C/div>

      \x3Cbutton type="submit" disabled={viewModel.submitDisabled}>
        {viewModel.submitLabel}
      \x3C/button>
    \x3C/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 \x3Cspan>{daysLeft} days remaining\x3C/span>;
});

// ✅ GOOD - ViewModel provides shaped result
const TaskCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
  return \x3Cspan>{viewModel.daysRemainingText}\x3C/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 \x3Cdiv>{data?.title}\x3C/div>;
});

// ✅ GOOD - Parent uses .bind() to invoke ServerRequest
\x3CTaskCard.bind({
  requestType: 'GetTaskRequest',
  params: { id: taskId }
}) />

Pattern: Error ViewModels Are Data

// ❌ BAD - Generic error handling
const TaskCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
  if (viewModel.error) {
    return \x3Cdiv>Error: {viewModel.error.message}\x3C/div>;
  }
  return \x3Cdiv>{viewModel.title}\x3C/div>;
});

// ✅ GOOD - Specific error ViewModels
const TaskCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
  if (viewModel.errorType === 'NotFoundError') {
    return (
      \x3Cdiv className="not-found">
        \x3Ch3>{viewModel.errorTitle}\x3C/h3>
        \x3Cp>{viewModel.errorMessage}\x3C/p>
        \x3Cp>{viewModel.suggestedAction}\x3C/p>
      \x3C/div>
    );
  }

  if (viewModel.errorType === 'ValidationError') {
    return (
      \x3Cdiv className="validation-error">
        \x3Ch3>{viewModel.errorTitle}\x3C/h3>
        \x3Cul>
          {viewModel.validationErrors.map(err => (
            \x3Cli key={err.field}>{err.message}\x3C/li>
          ))}
        \x3C/ul>
      \x3C/div>
    );
  }

  return \x3Cdiv>{viewModel.title}\x3C/div>;
});

Pattern: Navigation Intents

// ❌ BAD - Hardcoded URLs
const TaskRow = FOSMVVM.viewModelComponent(({ viewModel }) => {
  return (
    \x3Cdiv>
      \x3Ca href={`/tasks/${viewModel.id}`}>{viewModel.title}\x3C/a>
    \x3C/div>
  );
});

// ✅ GOOD - Navigation intents
// FOSMVVM utilities loaded via \x3Cscript> tag, available on global namespace

const TaskRow = FOSMVVM.viewModelComponent(({ viewModel }) => {
  return (
    \x3Cdiv>
      \x3CFOSMVVM.Link to={{ intent: 'viewTask', id: viewModel.id }}>
        {viewModel.title}
      \x3C/FOSMVVM.Link>
    \x3C/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 \x3Cdiv>{viewModel.firstName} {viewModel.lastName}\x3C/div>;
});

// ✅ GOOD - ViewModel provides shaped result
const UserCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
  return \x3Cdiv>{viewModel.fullName}\x3C/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 \x3Cdiv>{tasks.map(t => \x3Cdiv key={t.id}>{t.title}\x3C/div>)}\x3C/div>;
});

// ✅ GOOD - Parent uses .bind() with ServerRequest
\x3CTaskList.bind({
  requestType: 'GetTasksRequest',
  params: {}
}) />

Hardcoding Text

// ❌ BAD - Not localizable
const TaskCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
  return (
    \x3Cbutton onClick={viewModel.operations.submit}>
      Submit
    \x3C/button>
  );
});

// ✅ GOOD - ViewModel provides localized text
const TaskCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
  return (
    \x3Cbutton onClick={viewModel.operations.submit}>
      {viewModel.submitLabel}
    \x3C/button>
  );
});

Using Hardcoded URLs

// ❌ BAD - Hardcoded path
const TaskRow = FOSMVVM.viewModelComponent(({ viewModel }) => {
  return \x3Ca href={`/tasks/${viewModel.id}`}>{viewModel.title}\x3C/a>;
});

// ✅ GOOD - Navigation intent
// FOSMVVM utilities loaded via \x3Cscript> tag, available on global namespace

const TaskRow = FOSMVVM.viewModelComponent(({ viewModel }) => {
  return (
    \x3CFOSMVVM.Link to={{ intent: 'viewTask', id: viewModel.id }}>
      {viewModel.title}
    \x3C/FOSMVVM.Link>
  );
});

Not Wrapping with viewModelComponent()

// ❌ BAD - Missing viewModelComponent() wrapper
const TaskCard = ({ viewModel }) => {
  return \x3Cdiv>{viewModel.title}\x3C/div>;
};
export default TaskCard;

// ✅ GOOD - Wrapped with viewModelComponent()
const TaskCard = FOSMVVM.viewModelComponent(({ viewModel }) => {
  return \x3Cdiv>{viewModel.title}\x3C/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(\x3CTaskCard 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(\x3CTaskCard 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(\x3CTaskCard 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
Usage Guidance
This skill appears to be exactly a template-based generator for FOSMVVM React view components and is internally consistent. Before installing or using it: 1) Confirm your project actually uses the global FOSMVVM runtime (script tag / WASM bridge) because generated components rely on FOSMVVM on the global namespace. 2) Review generated files before committing — these are templates and may need adapting to your codebase (naming, imports, routing). 3) Avoid pasting secrets or sensitive configuration into the conversation or spec files you provide as input; the skill uses conversational/spec context to generate code. 4) Because this is an instruction-only skill, it won't install binaries or ask for credentials, but inspect generated outputs and run your test suite locally to validate behavior.
Capability Analysis
Type: OpenClaw Skill Name: fosmvvm-react-view-generator Version: 2.0.6 The skill is classified as **benign**. The `SKILL.md` and `reference.md` files provide detailed instructions and templates for an AI agent to generate React components and their corresponding tests within the FOSMVVM architectural pattern. The content is entirely focused on code generation, architectural guidelines, and best practices (e.g., avoiding direct `fetch()` calls, hardcoded URLs, and business logic in components). There is no evidence of malicious intent, such as attempts to exfiltrate data, execute arbitrary commands, establish persistence, or perform unauthorized actions. The instructions for the AI agent are clear and do not contain any prompt injection attempts designed to subvert the agent's purpose or security controls. The generated code templates are standard React and Jest code, lacking any inherent vulnerabilities or malicious constructs.
Capability Assessment
Purpose & Capability
Name/description (generate React components for FOSMVVM ViewModels) lines up with the SKILL.md and reference templates. The skill does not request unrelated binaries, environment variables, or config paths. All required artifacts (tests, .jsx/.test templates, use of global FOSMVVM) are coherent with the described purpose.
Instruction Scope
SKILL.md instructs the agent to generate test files first and then component files using provided templates and conversational context. It does not instruct reading arbitrary system files or accessing credentials. Two minor notes: (1) the doc repeatedly says the generator is "context-aware" and may reference conversation-provided specification files — if you paste private data/specs into the conversation that data will be used in generation, so avoid sharing secrets in chat; (2) the skill assumes FOSMVVM utilities are available on the global namespace (script tag / WASM bridge), so generated code will depend on that runtime.
Install Mechanism
Instruction-only skill with no install spec and no code files to write during install — lowest-risk category. Nothing is downloaded or executed during installation.
Credentials
No environment variables, credentials, or config paths are requested. Templates reference global FOSMVVM at runtime (a runtime dependency, not an env secret), which is reasonable for this generator.
Persistence & Privilege
always is false and the skill does not request persistent system configuration or elevated privileges. Autonomous invocation is allowed by default but that is normal for skills; there is no sign of this skill attempting to modify other skills or system-wide settings.
How to Use
  1. Make sure OpenClaw is installed (local or Docker)
  2. Run the install command in chat: /install fosmvvm-react-view-generator
  3. After installation, invoke the skill by name or use /fosmvvm-react-view-generator
  4. Provide required inputs per the skill's parameter spec and get structured output
Version History
v2.0.6
Initial ClawHub release
Metadata
Slug fosmvvm-react-view-generator
Version 2.0.6
License
All-time Installs 0
Active Installs 0
Total Versions 1
Frequently Asked Questions

What is FOSMVVM React View Generator?

Generate React components rendering FOSMVVM ViewModels with hooks, loading states, TypeScript types, test-first scaffolding, and .bind() server request integ... It is an AI Agent Skill for Claude Code / OpenClaw, with 626 downloads so far.

How do I install FOSMVVM React View Generator?

Run "/install fosmvvm-react-view-generator" in the OpenClaw or Claude Code chat to install it in one step — no extra setup required.

Is FOSMVVM React View Generator free?

Yes, FOSMVVM React View Generator is completely free (open-source). You can download, install and use it at no cost.

Which platforms does FOSMVVM React View Generator support?

FOSMVVM React View Generator is cross-platform and runs anywhere OpenClaw / Claude Code is available (darwin, linux).

Who created FOSMVVM React View Generator?

It is built and maintained by David Hunt (@foscomputerservices); the current version is v2.0.6.

💬 Comments