Requirements
- Target platform
- OpenClaw
- Install method
- Manual import
- Extraction
- Extract archive
- Prerequisites
- OpenClaw
- Primary doc
- SKILL.md
Generate React components rendering FOSMVVM ViewModels with hooks, loading states, TypeScript types, test-first scaffolding, and .bind() server request integ...
Generate React components rendering FOSMVVM ViewModels with hooks, loading states, TypeScript types, test-first scaffolding, and .bind() server request integ...
Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.
I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete.
I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run.
Generate React components that render FOSMVVM ViewModels.
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.
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
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.
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
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
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
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
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
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;
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;
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;
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
Two files per invocation: FileLocationPurpose{ViewName}View.test.jssrc/components/{Feature}/Jest + React Testing Library tests{ViewName}View.jsxsrc/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).
PlaceholderDescriptionExample{ViewName}View name (without "View" suffix)TaskList, SignIn{Feature}Feature/module groupingTasks, Auth
This skill references conversation context to determine component structure:
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
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
Generates .jsx following patterns: Import viewModelComponent wrapper 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
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
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)
// β 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>; });
// β 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 } }) />
// β 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>; });
// β 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> ); });
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
// β 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>; });
// β 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: {} }) />
// β 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> ); });
// β 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> ); });
// β 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;
// β BAD - Filename doesn't match ViewModel ViewModel: TaskListViewModel Component: Tasks.jsx // β GOOD - Aligned names ViewModel: TaskListViewModel Component: TaskListView.jsx
See reference.md for complete file templates.
ConceptConventionExampleComponent file{Name}View.jsxTaskListView.jsx, SignInView.jsxTest file{Name}View.test.jsTaskListView.test.jsComponent function{Name}ViewTaskListView, SignInViewViewModel propviewModelAlways 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(); });
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(); });
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(); });
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.
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
VersionDateChanges1.02026-01-23Initial skill for React view generation based on Kairos requirements
Code helpers, APIs, CLIs, browser automation, testing, and developer operations.
Largest current source with strong distribution and engagement signals.