AI Tools Compared

Cursorules are a powerful way to codify your team’s React component composition patterns. When configured correctly, they ensure that AI coding assistants generate consistent, maintainable components that align with your architecture. This guide walks you through creating effective Cursorules specifically designed for enforcing React component composition patterns across your team.

Why Component Composition Patterns Matter

React’s composition model gives developers flexibility in how they structure components. However, this flexibility can lead to inconsistency when multiple team members work on the same codebase. Without clear guidelines, you might encounter prop drilling, inconsistent component hierarchies, or mixed patterns for handling shared state.

Cursorules solve this problem by providing AI assistants with explicit instructions about your team’s preferred patterns. When an AI understands your composition conventions, it generates code that fits into your existing architecture.

The problem compounds as teams grow. A five-person team might handle inconsistency informally through code reviews. A twenty-person team cannot. When developers on different squads build features in parallel, diverging patterns create merge conflicts, confuse new hires, and slow down refactoring efforts. Cursorules act as a standing policy document that every AI-assisted session respects automatically, without requiring reviewers to catch every deviation.

Defining Your Component Composition Rules

Before writing Cursorules, document your team’s composition patterns. Consider these questions:

Once you have clear answers, translate them into Cursorules that AI assistants can follow.

A practical exercise: audit five recent components your team wrote and identify the patterns they share. If three out of five use compound components with Context for internal state, that is your preferred pattern. If naming conventions drift across those five, that inconsistency is exactly what Cursorules can lock down.

Creating Effective Cursorules

Here is an example of Cursorules designed to enforce compound component patterns:

# Cursorules for React Component Composition

## Compound Component Patterns

When creating components that need to share state between parent and children, use compound component patterns:

1. Use a parent component that manages state with React Context
2. Export child components as static properties of the parent
3. Use implicit state passing through Context

Example structure:
- ButtonGroup (parent, manages selected state)
- ButtonGroup.Button (child component)
- ButtonGroup.ButtonProps (optional, for TypeScript)

## Prop Naming Conventions

- Use 'children' for content slots, never 'content' or 'body'
- Prefix callback props with 'on' (onClick, onChange)
- Use 'is' or 'has' prefix for boolean props (isDisabled, hasError)
- Prefix internal props with underscore (_internalState)

## Component File Organization

Each component should follow this structure:
1. Type definitions (if TypeScript)
2. Context creation
3. Child component definitions
4. Parent component with composition
5. Named exports

## Forbidden Patterns

- Never use props.children for component logic
- Avoid passing components as props (use compound components instead)
- Don't create HOCs - prefer custom hooks

Enforcing Container-Presenter Pattern

Many teams adopt the container-presenter pattern for separating logic from presentation. Here is how to encode this in Cursorules:

## Container-Presenter Separation

When building features, separate concerns using containers and presenters:

Container responsibilities:
- Manage state and side effects
- Handle data fetching
- Pass raw data and callbacks to presenter

Presenter responsibilities:
- Receive data as props
- Handle UI logic only
- Be purely presentational when possible

Example:
// Container
function UserListContainer() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    fetchUsers().then(setUsers);
  }, []);

  return <UserListPresenter users={users} />;
}

// Presenter
function UserListPresenter({ users }) {
  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

This pattern pairs well with testing strategies. Presenters are trivial to unit test because they are pure functions of their props. Containers can be tested separately using mocked data fetching. When Cursor generates code following this rule, your test coverage naturally improves alongside consistency.

Handling Component Composition in Custom Hooks

Custom hooks have become the preferred way to share logic in React applications. Your Cursorules should specify how AI assistants should create and use hooks:

## Custom Hooks Guidelines

1. Always prefix hook names with 'use' (useAuth, useFetch)
2. Return arrays for tuple-like data, objects for named properties
3. Document dependencies in comments when using useEffect
4. Handle loading and error states consistently

Example return pattern:
const { data, loading, error, refetch } = useUserData(userId);

An important nuance: hooks that manage a single value with a setter should return a tuple, like useState itself. Hooks that return multiple named properties should return an object. This distinction prevents destructuring confusion and makes call sites readable at a glance. Include this distinction explicitly in your Cursorules so the AI applies the right return shape automatically.

Structuring TypeScript Interfaces for Consistency

Teams using TypeScript benefit from encoding interface conventions in Cursorules. Without explicit guidance, AI assistants may generate redundant interfaces, misplace type definitions, or use inconsistent naming patterns across components.

Add a section like this to your Cursorules:

## TypeScript Interface Conventions

- Define component prop interfaces immediately above the component they describe
- Name prop interfaces as {ComponentName}Props (e.g., ButtonProps, ModalProps)
- Separate complex types into a dedicated Types.ts file if they are shared
- Prefer `interface` over `type` for object shapes
- Use `type` for unions, intersections, and primitive aliases

## Generic Component Patterns

When writing generic components, constrain type parameters:
- Prefer <T extends object> over unconstrained <T>
- Document generic parameters in JSDoc when the constraint is non-obvious

This prevents a common AI antipattern where Cursor generates a type Props = {} at the bottom of a file, inconsistent with where other interfaces live.

Testing Your Cursorules

After writing your Cursorules, test them by generating sample components. Ask your AI assistant to create a component following your rules, then verify:

Iterate on your Cursorules based on what the AI generates versus what you expect.

A systematic testing approach: create a checklist with one item per rule in your Cursorules file. After generating a test component, walk through the checklist item by item. Rules that the AI violates need either more specific wording or concrete examples. Rules the AI follows consistently can be marked stable. Treat Cursorules like code: they need testing and revision.

Sharing Cursorules Across Your Team

Place your Cursorules file in your project root as .cursorrules or .cursor/rules. Ensure every team member uses the same file by:

Regular updates to your Cursorules should follow your standard code review process.

Consider treating Cursorules changes the same way you treat changes to your ESLint configuration. Both define coding standards; both warrant a PR, a short discussion, and explicit team sign-off. When a new pattern emerges organically in your codebase, a Cursorules update formalizes it and propagates it to every subsequent AI-assisted session automatically.

Example: Complete Cursorules File

# Project React Composition Guidelines

## Overview

This file defines our team's React component composition patterns.
All AI-generated code should follow these guidelines.

## Component Types

### Presentational Components
- Focus on UI only
- Receive data and callbacks as props
- No side effects or state (use useMemo/useCallback for optimization)
- Export as named exports

### Container Components
- Handle data fetching and state management
- Pass data to presentational components
- May use custom hooks internally

### Compound Components
- Use React Context for internal state
- Export child components as static properties
- Example: <Modal><Modal.Header /><Modal.Body /><Modal.Footer /></Modal>

## Props Pattern

```tsx
interface ButtonProps {

 variant?: 'primary' | 'secondary' | 'danger';

 size?: 'sm' | 'md' | 'lg';

 isDisabled?: boolean;

 onClick?: () => void;

 children: React.ReactNode;

}

Import Order

  1. React imports
  2. External libraries
  3. Internal imports (absolute paths)
  4. Relative imports
  5. Type imports

File Naming

Keeping Cursorules Lean and Effective

One pitfall is writing Cursorules that are too long. If your rules file exceeds 200 lines, the AI assistant may weight early rules more heavily than later ones, or fail to apply all rules simultaneously. Keep each rule concise and actionable.

Prefer concrete examples over abstract descriptions. Instead of writing “use appropriate naming conventions,” write “name boolean props with is or has prefix: isDisabled, hasError, isLoading.” The more specific the instruction, the more reliably Cursor applies it.

Review your Cursorules quarterly. As React itself evolves — new hooks, new patterns, new best practices — your rules should evolve alongside it. A Cursorules file that references patterns from two years ago may actively guide the AI toward outdated approaches.

Built by theluckystrike — More at zovo.one