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
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.
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.
'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