Architecture enforcement for MVVM patterns in React Native applications.

Install

mkdir -p .claude/skills/mvvm && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/17262" && unzip -o skill.zip -d .claude/skills/mvvm && rm skill.zip

Installs to .claude/skills/mvvm

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Enforce MVVM architecture in React Native/Expo apps, especially screens, hooks as ViewModels, services, and code reviews.
121 charsno explicit “when” trigger
Intermediate

Key capabilities

  • Enforce MVVM architecture in React Native/Expo apps
  • Isolate Model, ViewModel, and View layers
  • Define naming conventions for ViewModels
  • Move business logic from components to ViewModels
  • Perform code reviews for MVVM compliance

How it works

The skill enforces the MVVM pattern by defining rules for Model, ViewModel, and View layers, including file locations, naming conventions, and responsibilities. It provides a refactoring checklist to migrate existing code to MVVM.

Inputs & outputs

You give it
A React Native/Expo app codebase, or a request to create/modify a screen
You get back
A React Native/Expo app structured according to MVVM architecture, or a refactoring plan

When to use mvvm

  • Structuring a new React Native screen
  • Moving logic from components to ViewModels
  • Performing code reviews for MVVM compliance
  • Creating domain-specific services

About this skill

MVVM Architecture Skill - React Native / Expo

Purpose

Enforce Model-View-ViewModel (MVVM) pattern across the entire codebase. Every screen and component must separate concerns cleanly.

Layer Definitions

Model (Data Layer)

  • Location: types/, src/services/, src/sync/
  • Contains: TypeScript types/interfaces, API clients, AsyncStorage persistence, data transformation
  • Rules:
    • No React imports
    • No UI logic
    • Pure functions and async service calls
    • One service file per domain entity (e.g. gruposService.ts, planeacionesService.ts)

ViewModel (Logic Layer)

  • Location: src/hooks/ (one file per screen or shared domain)
  • Naming: use{ScreenName}ViewModel.ts (e.g. useCrearGrupoViewModel.ts)
  • Contains: all useState, useEffect, useCallback, useMemo, derived state, validation, business logic, navigation triggers
  • Rules:
    • Custom hook that returns a typed interface
    • No JSX
    • No StyleSheet, no layout logic
    • May call services, context, other hooks
    • Must define a return type interface (e.g. CrearGrupoViewModel)
    • All event handlers live here (onSave, onDelete, onChange, etc.)
    • Navigation calls go here when triggered by logic (e.g. after save)

View (Presentation Layer)

  • Location: src/screens/, src/components/
  • Contains: JSX rendering, StyleSheet, layout
  • Rules:
    • Call the ViewModel hook at the top: const vm = useXxxViewModel()
    • Destructure only what is needed from the ViewModel
    • No useState for business state (UI-only state like scrollY or menuVisible is allowed)
    • No direct service calls
    • No async operations
    • No data transformation or validation logic
    • Bind ViewModel properties to JSX: <Text>{vm.title}</Text>
    • Bind ViewModel handlers to events: <Button onPress={vm.onSave} />

ViewModel Template

// src/hooks/useExampleViewModel.ts
import { useState, useCallback } from "react";
import { useNavigation } from "@react-navigation/native";
import type { NativeStackNavigationProp } from "@react-navigation/native-stack";
import type { RootStackParamList } from "../navigation/StackNavigator";

export interface ExampleViewModel {
  // State
  title: string;
  isLoading: boolean;
  error: string | null;

  // Actions
  setTitle: (value: string) => void;
  onSave: () => Promise<void>;
}

export const useExampleViewModel = (): ExampleViewModel => {
  const navigation =
    useNavigation<NativeStackNavigationProp<RootStackParamList>>();
  const [title, setTitle] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const onSave = useCallback(async () => {
    setIsLoading(true);
    try {
      // call service
      navigation.goBack();
    } catch (err) {
      setError(String(err));
    } finally {
      setIsLoading(false);
    }
  }, [title, navigation]);

  return { title, isLoading, error, setTitle, onSave };
};

When This Skill Applies

  • Creating any new screen or component
  • Modifying any existing screen
  • Reviewing pull requests
  • Refactoring code

Refactoring Checklist

When modifying a screen that does NOT follow MVVM:

  1. Create src/hooks/use{Screen}ViewModel.ts
  2. Move all useState (except UI-only state) into the ViewModel
  3. Move all useEffect with business logic into the ViewModel
  4. Move all handler functions into the ViewModel
  5. Move all service/context calls into the ViewModel
  6. Define a typed return interface
  7. In the screen, call the hook and bind to JSX
  8. Verify the screen file contains only: JSX, StyleSheet, UI-only state

Exceptions

  • Pure navigation screens (no state, just menu cards that navigate): ViewModel is optional if the screen has zero state and zero logic. Still recommended for consistency.
  • UI-only state: menuVisible, scrollPosition, expanded toggles that control layout can stay in the View.
  • Shared components (SemanaEditor, EvaluacionEditor): May keep local UI state for controlled inputs. Business logic should be in the parent ViewModel, passed down as callbacks via props.

File Organization

src/
  hooks/
    useGrupos.ts
    useLoginViewModel.ts
    useHomeViewModel.ts
    useCrearPlaneacionViewModel.ts
    useEditorPlaneacionViewModel.ts
    useListaPlaneacionesViewModel.ts
    useCrearGrupoViewModel.ts
    useListaGruposViewModel.ts
    useDetalleGrupoViewModel.ts
    useCrearTareaGrupoViewModel.ts
    useCalificarEntregasViewModel.ts
    useListaRecursosViewModel.ts
    useCuentaViewModel.ts
  screens/
    (Views only - JSX + StyleSheet)
  services/
    (Model - data access)
  types/
    (Model - type definitions)

Do Not

  • Put useState for domain data inside a screen component
  • Call services directly from a screen component
  • Put navigation logic inside JSX event handlers inline
  • Skip the typed return interface on ViewModels
  • Name ViewModel files without the use prefix

When not to use it

  • When `useState` for domain data is inside a screen component
  • When services are called directly from a screen component
  • When navigation logic is inside JSX event handlers inline

Limitations

  • ViewModels must be custom hooks that return a typed interface.
  • Views should not contain `useState` for business state.
  • Models should not contain React imports or UI logic.

How it compares

This skill provides a structured approach to implementing MVVM in React Native/Expo, ensuring clear separation of concerns and maintainability, which is more disciplined than ad-hoc component development.

Compared to similar skills

mvvm side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
mvvm (this skill)019dNo flagsIntermediate
react-native-architecture552moReviewAdvanced
react-native-architecture03moNo flagsIntermediate
mobile-state-management-patterns03moNo flagsIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry