Comparisons

React vs Vue: AI Rules Comparison

React and Vue have fundamentally different reactivity models, component APIs, and state management. Each needs specific AI rules to prevent the AI from generating the wrong framework's patterns. Side-by-side rules with templates.

8 min read·April 24, 2025

useState in a Vue component, v-if in JSX — without framework rules, every component is wrong

JSX vs SFC, hooks vs Composition API, state paradigms, ecosystem rules, and copy-paste templates

Different Paradigms, Different Rules

React and Vue are both component-based UI frameworks, but their core paradigms differ. React uses: JSX (JavaScript XML for templates), hooks for state and lifecycle (useState, useEffect, useMemo), unidirectional data flow (props down, events up), and explicit re-rendering (state changes trigger re-render of the component and children). Vue uses: Single-File Components with <template>/<script>/<style> blocks, Composition API for state and lifecycle (ref, reactive, computed, watch), reactive data binding (changes to reactive state automatically update the DOM), and fine-grained reactivity (only affected DOM nodes update, not the entire component).

Without framework-specific rules, AI generates: React hooks in Vue components (useEffect does not exist in Vue), Vue template syntax in React JSX (v-if does not work in JSX), React state patterns in Vue (useState instead of ref), or Vue reactive patterns in React (reactive() is not a React API). The frameworks look similar at a distance (both have components, props, and state) but the APIs are entirely different. One framework rule prevents every cross-contamination error.

This article provides: the specific AI rules needed for each framework, side-by-side comparisons of equivalent patterns, and copy-paste CLAUDE.md templates. The rules tell the AI which framework is in use and which patterns to follow — eliminating the most common source of framework-confused code generation.

Template Syntax: JSX vs Single-File Components

React uses JSX: JavaScript expressions embedded in XML-like syntax. Conditional rendering: {isLoading ? <Spinner /> : <Content />}. List rendering: {items.map(item => <Item key={item.id} />)}. Event handling: onClick={handleClick}. Class names: className (not class). JSX is JavaScript — you use ternaries for conditionals, .map() for loops, and template literals for dynamic strings. AI rule: 'Use JSX for templates. Conditional: ternary or &&. Lists: .map() with key prop. Events: camelCase (onClick, onChange). className not class.'

Vue uses Single-File Components (SFC): <template> for HTML with Vue directives, <script setup> for logic, <style scoped> for CSS. Conditional rendering: <div v-if="isLoading">Loading</div><div v-else>Content</div>. List rendering: <Item v-for="item in items" :key="item.id" />. Event handling: @click="handleClick". Class binding: :class="{ active: isActive }". AI rule: 'Use SFC with <template>, <script setup>, <style scoped>. Conditionals: v-if/v-else. Lists: v-for with :key. Events: @ shorthand (@click, @input). Use class not className.'

The template rule is the most impactful: AI generating v-if in JSX produces a syntax error. AI generating .map() in a Vue template produces incorrect output. AI using className in Vue adds a non-standard attribute. The template syntax is the first thing the AI must get right — every other pattern builds on it. One rule about JSX vs SFC prevents the entire class of template syntax errors.

  • React: JSX with JavaScript expressions — ternary for conditionals, .map() for lists
  • Vue: SFC with directives — v-if for conditionals, v-for for lists, @ for events
  • React: className, onClick, onChange — camelCase attributes
  • Vue: class, @click, @input — HTML attributes with directive shortcuts
  • Cross-contamination: v-if in JSX = error, .map() in Vue template = wrong pattern
💡 Template Syntax Is Rule #1

v-if in JSX = syntax error. .map() in Vue template = wrong pattern. className in Vue = non-standard attribute. The template syntax is the first thing the AI must get right — every other pattern builds on it. One rule about JSX vs SFC prevents the entire class of template errors.

State and Reactivity: Hooks vs Composition API

React state: const [count, setCount] = useState(0). Update: setCount(count + 1) or setCount(prev => prev + 1). Effect: useEffect(() => { fetchData(); }, [dependency]). Computed: const total = useMemo(() => items.reduce(...), [items]). React state is: immutable (replace the value, do not mutate it), hook-based (call hooks at the top level of the component), and dependency-tracked (useEffect and useMemo re-run when dependencies change).

Vue state: const count = ref(0). Update: count.value++ (direct mutation — Vue detects changes automatically). Effect: watch(dependency, () => { fetchData(); }) or watchEffect(() => { /* auto-tracks reactive deps */ }). Computed: const total = computed(() => items.value.reduce(...)). Vue state is: mutable (change the .value directly), reactive (changes are detected automatically without dependency arrays), and fine-grained (only the DOM nodes that read the value update).

The state rule is critical: AI generating setCount(count + 1) in Vue produces undefined function errors. AI generating count.value++ in React mutates state directly (React does not detect mutations). The mental models are opposite: React = immutable replacement, Vue = mutable reactive proxy. The rule tells the AI: 'React: never mutate state, use setState. Vue: mutate .value directly, Vue detects changes.' One sentence prevents every state management error.

⚠️ Opposite Mental Models

React: never mutate state, call setState to replace. Vue: mutate .value directly, reactivity detects changes automatically. AI generating count.value++ in React: silent mutation, component does not re-render. AI generating setCount in Vue: function does not exist. One sentence prevents every state error.

Component Patterns: Props, Events, Slots

React props and events: props are passed as JSX attributes, including callback functions. <Button onClick={handleClick} label="Submit" disabled={isLoading} />. There is no distinction between props and events — event handlers are just props that happen to be functions. Children: {children} prop or React.createElement children. Slots: use children or render props (function-as-child pattern).

Vue props and events: props are declared with defineProps, events with defineEmits. <Button @click="handleClick" label="Submit" :disabled="isLoading" />. Props flow down, events emit up — explicit separation. Children: <slot /> in the component template. Named slots: <slot name="header" />. The prop/event/slot system is more structured than React's everything-is-a-prop approach.

The pattern rule: 'React: pass event handlers as props (onClick, onChange). No emit system — parent passes callback, child calls it. Children via {children} prop.' 'Vue: declare props with defineProps, events with defineEmits. Emit events: emit("update", value). Slots: <slot /> for children, <slot name="header" /> for named slots.' The AI must know: React has no emit (call the prop function), Vue has no children prop (use <slot />).

  • React: events are props (onClick={fn}), children via {children}, render props for slots
  • Vue: defineProps + defineEmits, @click for events, <slot /> for children, named slots
  • React: no emit system — parent passes callback, child calls it directly
  • Vue: no children prop — use <slot /> in template, <template #header> for named slots
  • AI mixing: emit() in React = undefined. {children} in Vue = wrong pattern

Ecosystem: Routing, State Management, Meta-Frameworks

React ecosystem rules: 'Router: React Router (useNavigate, Link, Route) or Next.js App Router (file-based routing, server components). State management: TanStack Query for server state, Zustand or Jotai for client state (not Redux unless already in the project). Meta-framework: Next.js (default for new projects) with App Router and Server Components. Styling: Tailwind CSS (default), CSS Modules as alternative.'

Vue ecosystem rules: 'Router: Vue Router (useRouter, RouterLink, defineRoute). State management: Pinia (official, replaces Vuex). Meta-framework: Nuxt (default for new projects) with auto-imports and file-based routing. Styling: Tailwind CSS (default), scoped styles in SFC as alternative. Composables: use composable functions (useAuth, useFetch) following Vue naming conventions (use prefix).'

The ecosystem rule prevents: AI suggesting Next.js patterns in a Nuxt project (different file structure, different conventions), recommending Redux in a Vue project (Pinia is the Vue standard), or using React Router APIs in a Vue Router project. The meta-framework choice cascades into file structure, routing, data fetching, and deployment patterns. One ecosystem rule paragraph aligns the AI with the entire project stack.

Ready-to-Use Rule Templates

React CLAUDE.md template: '# Framework (React). This project uses React 19 with Next.js App Router. Components: functional components with hooks (no class components). State: useState for local, TanStack Query for server, Zustand for global client state. Templates: JSX with ternary conditionals and .map() for lists. Events: camelCase props (onClick, onChange). Styling: Tailwind CSS with cn() utility. Testing: Vitest + React Testing Library. Never use Vue syntax (v-if, v-for, defineProps, emit).'

Vue CLAUDE.md template: '# Framework (Vue). This project uses Vue 3 with Nuxt. Components: <script setup> with Composition API (no Options API). State: ref() for primitives, reactive() for objects, Pinia for global state. Templates: <template> with v-if/v-for directives. Events: defineEmits + @ shorthand. Slots: <slot /> for children, named slots for composition. Styling: Tailwind CSS + scoped styles. Testing: Vitest + Vue Test Utils. Never use React syntax (useState, useEffect, JSX, className).'

The negative rules are as important as the positive ones: 'Never use Vue syntax' in a React project and 'Never use React syntax' in a Vue project explicitly tell the AI what NOT to generate. Without the negative rule: the AI may blend familiar patterns from either framework. With it: the AI has a clear boundary. Copy the template for your framework — the positive rules guide correct generation, the negative rules prevent cross-contamination.

ℹ️ Negative Rules Are as Important as Positive

'Never use Vue syntax' in a React project explicitly tells the AI what NOT to generate. Without it: the AI may blend familiar patterns from either framework. The negative rule creates a clear boundary. Positive rules guide; negative rules prevent.

Comparison Summary

Summary of React vs Vue AI rules.

  • Templates: React JSX (ternary, .map(), className) vs Vue SFC (v-if, v-for, class, @click)
  • State: React useState (immutable replacement) vs Vue ref (mutable .value, reactive proxy)
  • Effects: React useEffect + deps array vs Vue watch/watchEffect (auto-tracked reactivity)
  • Props/Events: React everything-is-a-prop vs Vue defineProps + defineEmits separation
  • Children: React {children} prop vs Vue <slot /> in template
  • Ecosystem: Next.js + TanStack Query + Zustand vs Nuxt + Pinia + Vue Router
  • Negative rules: 'Never use Vue syntax' in React, 'Never use React syntax' in Vue
  • Templates: one paragraph per framework prevents all cross-contamination errors