# How to Make Cursor Follow a Specific Coding Style

- Tool: Cursor
- Difficulty: Beginner
- Time required: 10-15 min
- Compatibility: Cursor Free+, JavaScript/TypeScript projects
- Last updated: March 2026

## TL;DR

Cursor defaults to imperative and object-oriented patterns in JavaScript because those dominate its training data. By creating .cursor/rules/ entries that specify functional programming conventions like pure functions, immutability, and composition over inheritance, and by prompting with explicit FP vocabulary, you can consistently get Cursor to generate code that follows your team's functional style.

## Making Cursor follow your preferred coding style

Every team has coding conventions, but Cursor does not know yours until you tell it. Whether your team prefers functional programming, OOP, or a hybrid approach, project rules and targeted prompts are how you enforce consistency. This tutorial focuses on functional programming patterns but the technique applies to any style preference.

## Before you start

- Cursor installed with a JavaScript or TypeScript project
- Basic understanding of functional programming concepts
- Familiarity with .cursor/rules/ directory structure
- Understanding of Cmd+K, Cmd+L, and Cmd+I shortcuts

## Step-by-step guide

### 1. Create a functional programming style rule

Define a .cursor/rules/ file that specifies your team's functional programming conventions. List both required and forbidden patterns. The more specific you are, the more consistently Cursor follows the rules across sessions.

```
---
description: Enforce functional programming patterns
globs: "*.ts,*.tsx,*.js,*.jsx"
alwaysApply: true
---

# Functional Programming Style

## ALWAYS:
- Write pure functions with no side effects
- Use const for all declarations (never let or var)
- Use Array.map, .filter, .reduce instead of for loops
- Return new objects/arrays instead of mutating existing ones
- Use function composition and pipe patterns
- Prefer readonly types in TypeScript
- Use discriminated unions over class hierarchies

## NEVER:
- Mutate function arguments
- Use class keyword (use plain objects and functions)
- Use this keyword
- Use for, while, or do-while loops
- Use push, pop, splice, or other mutating array methods
- Use delete operator on objects

## Pattern:
```typescript
// Correct: pure function, immutable
const addItem = (items: readonly Item[], newItem: Item): readonly Item[] =>
  [...items, newItem];

// Wrong: mutation
const addItem = (items: Item[], newItem: Item): void => {
  items.push(newItem);
};
```
```

**Expected result:** Cursor generates code using pure functions, immutable data, and array methods instead of loops and mutations.

### 2. Test with a Chat prompt using FP vocabulary

Open Cmd+L and prompt Cursor with a task, using FP-specific terms. Words like 'pure function', 'immutable', 'compose', and 'pipeline' signal to the model that you want functional patterns. Reference your rule file explicitly.

```
@functional-style.mdc

Write a data transformation pipeline for processing user records:
1. A pure function to filter users older than 18
2. A pure function to normalize email addresses to lowercase
3. A pure function to group users by their country
4. A compose/pipe utility that chains these transformations

All functions must be pure with no side effects.
Use readonly types. Return new arrays, never mutate.
```

**Expected result:** Cursor generates a pipeline of pure functions using Array.filter, Array.map, and Array.reduce with readonly types and a pipe utility.

### 3. Refactor imperative code to functional style with Cmd+K

Select a block of imperative code in your editor and press Cmd+K. Use a prompt that explicitly asks for functional transformation. Cursor will rewrite loops as array methods, mutations as immutable operations, and classes as plain functions.

```
Rewrite this code in functional programming style:
- Replace all for loops with .map(), .filter(), or .reduce()
- Replace mutations with immutable operations (spread operator)
- Replace class with pure functions and plain objects
- Remove all uses of 'this', 'let', and 'var'
- Keep all behavior identical
```

> Pro tip: If Cursor partially converts the code, select the remaining imperative sections and run Cmd+K again with the same prompt. Smaller selections produce more accurate results.

**Expected result:** The selected code is rewritten using functional patterns while maintaining identical behavior.

### 4. Create a utility file for common FP patterns

Give Cursor a reference file with pipe, compose, and other utility functions. When Cursor sees these in your project via @file, it uses them in generated code instead of reinventing them or importing from third-party libraries you may not want.

```
export const pipe = <T>(...fns: Array<(arg: T) => T>) =>
  (value: T): T =>
    fns.reduce((acc, fn) => fn(acc), value);

export const compose = <T>(...fns: Array<(arg: T) => T>) =>
  (value: T): T =>
    fns.reduceRight((acc, fn) => fn(acc), value);

export const mapArray = <T, U>(fn: (item: T) => U) =>
  (arr: readonly T[]): readonly U[] =>
    arr.map(fn);

export const filterArray = <T>(predicate: (item: T) => boolean) =>
  (arr: readonly T[]): readonly T[] =>
    arr.filter(predicate);

export const prop = <T, K extends keyof T>(key: K) =>
  (obj: T): T[K] =>
    obj[key];
```

**Expected result:** Cursor imports pipe, compose, and other utilities from this file when generating functional code.

### 5. Enforce style in Composer Agent for multi-file generation

When generating entire features with Composer (Cmd+I), include the style rule and utility file references at the start of your prompt. Agent mode processes multiple files and needs the style context upfront to maintain consistency across all generated files.

```
@functional-style.mdc @src/lib/fp-utils.ts

Create a complete user management module with these files:
- src/users/types.ts (readonly interfaces, discriminated unions)
- src/users/validators.ts (pure validation functions)
- src/users/transformers.ts (pure data transformation functions)
- src/users/repository.ts (database access using the pipe pattern)

All functions must be pure. Use pipe from @/lib/fp-utils.
No classes, no mutations, no for loops.
```

**Expected result:** Cursor Agent generates four files all following functional patterns, using pipe/compose from the utility file and readonly types.

## Complete code example

File: `.cursor/rules/functional-style.mdc`

```markdown
---
description: Enforce functional programming patterns
globs: "*.ts,*.tsx,*.js,*.jsx"
alwaysApply: true
---

# Functional Programming Style

## ALWAYS:
- Write pure functions with no side effects
- Use const for all declarations (never let or var)
- Use Array.map, .filter, .reduce instead of for loops
- Return new objects/arrays instead of mutating existing ones
- Use function composition with pipe/compose from @/lib/fp-utils
- Prefer readonly types in TypeScript interfaces
- Use discriminated unions over class hierarchies
- Keep functions small (under 15 lines)
- Use early returns over nested conditionals

## NEVER:
- Mutate function arguments
- Use class keyword (use plain objects and functions)
- Use this keyword
- Use for, while, or do-while loops
- Use push, pop, splice, or other mutating array methods
- Use delete operator on objects
- Use null (prefer undefined or Option types)

## Immutable Update Patterns:
```typescript
const updateUser = (user: Readonly<User>, changes: Partial<User>): User =>
  ({ ...user, ...changes, updatedAt: new Date() });

const removeItem = (items: readonly Item[], index: number): readonly Item[] =>
  [...items.slice(0, index), ...items.slice(index + 1)];

const addItem = (items: readonly Item[], item: Item): readonly Item[] =>
  [...items, item];
```
```

## Common mistakes

- **Writing rules too broadly like 'use functional programming'** — Cursor interprets broad instructions inconsistently. It might avoid classes but still use for loops, or use map but still mutate objects. Fix: List every specific pattern: no for loops, no mutations, no classes, no this, no let/var. Include both forbidden and required patterns with examples.
- **Not providing utility functions for Cursor to import** — Without a pipe or compose function in the project, Cursor either inlines the utility every time or imports from a library you may not have installed. Fix: Create src/lib/fp-utils.ts with your core utilities and reference it with @file in prompts.
- **Applying FP rules to files that need imperative patterns** — Some code like database drivers, framework bootstrapping, or performance-critical loops is better written imperatively. Blanket FP rules can produce awkward code. Fix: Use the globs field to target only your application code directories. Create separate rules for infrastructure code that permit imperative patterns.

## Best practices

- Use FP-specific vocabulary in prompts: pure function, immutable, compose, pipeline, discriminated union
- Create a shared fp-utils.ts file that Cursor can import from consistently
- Set globs to target application code only, excluding config files and scripts
- Include concrete code examples in rules showing both correct and incorrect patterns
- Start new Chat sessions when Cursor drifts from functional style in long conversations
- Combine Cursor rules with TypeScript readonly types for compile-time immutability enforcement
- Review generated code for subtle mutations like array.sort() which mutates in place

## Frequently asked questions

### Does Cursor understand advanced FP concepts like monads?

Cursor understands basic FP patterns well but struggles with advanced category theory concepts. For monads, Either types, or IO patterns, provide explicit type definitions and examples in your rules file rather than relying on FP terminology alone.

### Should I use a library like fp-ts or Ramda with Cursor?

You can, but reference the library documentation with @docs so Cursor knows the correct API. Without docs context, Cursor may hallucinate function names or use incorrect signatures from older library versions.

### How do I handle side effects like API calls in functional style?

Separate pure logic from side effects. Create pure transformation functions and thin wrapper functions that perform I/O. Mention this pattern in your rules: pure core, impure shell.

### Will Tab completion follow functional style rules?

No. Tab completion uses a separate model that does not read project rules. Only Chat (Cmd+L), Composer (Cmd+I), and inline edit (Cmd+K) respect .cursor/rules/ files.

### Can I mix FP and OOP in the same project?

Yes. Use the globs field to apply FP rules to specific directories like src/domain/ and OOP rules to other directories. Different .mdc files can have different style requirements.

### Can RapidDev help establish coding standards for our team?

Yes. RapidDev helps teams define, document, and enforce coding standards through Cursor rules, linting configuration, and code review automation. This ensures consistent code quality across AI-assisted and manual development.

---

Source: https://www.rapidevelopers.com/cursor-tutorial/how-to-refine-cursor-ai-prompts-for-functional-programming-patterns-in-a-javascript-codebase
© RapidDev — https://www.rapidevelopers.com/cursor-tutorial/how-to-refine-cursor-ai-prompts-for-functional-programming-patterns-in-a-javascript-codebase
