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.
Prerequisites
- 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
Create a functional programming style rule
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.
1---2description: Enforce functional programming patterns3globs: "*.ts,*.tsx,*.js,*.jsx"4alwaysApply: true5---67# Functional Programming Style89## ALWAYS:10- Write pure functions with no side effects11- Use const for all declarations (never let or var)12- Use Array.map, .filter, .reduce instead of for loops13- Return new objects/arrays instead of mutating existing ones14- Use function composition and pipe patterns15- Prefer readonly types in TypeScript16- Use discriminated unions over class hierarchies1718## NEVER:19- Mutate function arguments20- Use class keyword (use plain objects and functions)21- Use this keyword22- Use for, while, or do-while loops23- Use push, pop, splice, or other mutating array methods24- Use delete operator on objects2526## Pattern:27```typescript28// Correct: pure function, immutable29const addItem = (items: readonly Item[], newItem: Item): readonly Item[] =>30 [...items, newItem];3132// Wrong: mutation33const addItem = (items: Item[], newItem: Item): void => {34 items.push(newItem);35};36```Expected result: Cursor generates code using pure functions, immutable data, and array methods instead of loops and mutations.
Test with a Chat prompt using FP vocabulary
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.
1@functional-style.mdc23Write a data transformation pipeline for processing user records:41. A pure function to filter users older than 1852. A pure function to normalize email addresses to lowercase63. A pure function to group users by their country74. A compose/pipe utility that chains these transformations89All functions must be pure with no side effects.10Use 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.
Refactor imperative code to functional style with Cmd+K
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.
1Rewrite this code in functional programming style:2- Replace all for loops with .map(), .filter(), or .reduce()3- Replace mutations with immutable operations (spread operator)4- Replace class with pure functions and plain objects5- Remove all uses of 'this', 'let', and 'var'6- Keep all behavior identicalPro 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.
Create a utility file for common FP patterns
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.
1export const pipe = <T>(...fns: Array<(arg: T) => T>) =>2 (value: T): T =>3 fns.reduce((acc, fn) => fn(acc), value);45export const compose = <T>(...fns: Array<(arg: T) => T>) =>6 (value: T): T =>7 fns.reduceRight((acc, fn) => fn(acc), value);89export const mapArray = <T, U>(fn: (item: T) => U) =>10 (arr: readonly T[]): readonly U[] =>11 arr.map(fn);1213export const filterArray = <T>(predicate: (item: T) => boolean) =>14 (arr: readonly T[]): readonly T[] =>15 arr.filter(predicate);1617export const prop = <T, K extends keyof T>(key: K) =>18 (obj: T): T[K] =>19 obj[key];Expected result: Cursor imports pipe, compose, and other utilities from this file when generating functional code.
Enforce style in Composer Agent for multi-file generation
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.
1@functional-style.mdc @src/lib/fp-utils.ts23Create a complete user management module with these files:4- src/users/types.ts (readonly interfaces, discriminated unions)5- src/users/validators.ts (pure validation functions)6- src/users/transformers.ts (pure data transformation functions)7- src/users/repository.ts (database access using the pipe pattern)89All functions must be pure. Use pipe from @/lib/fp-utils.10No 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 working example
1---2description: Enforce functional programming patterns3globs: "*.ts,*.tsx,*.js,*.jsx"4alwaysApply: true5---67# Functional Programming Style89## ALWAYS:10- Write pure functions with no side effects11- Use const for all declarations (never let or var)12- Use Array.map, .filter, .reduce instead of for loops13- Return new objects/arrays instead of mutating existing ones14- Use function composition with pipe/compose from @/lib/fp-utils15- Prefer readonly types in TypeScript interfaces16- Use discriminated unions over class hierarchies17- Keep functions small (under 15 lines)18- Use early returns over nested conditionals1920## NEVER:21- Mutate function arguments22- Use class keyword (use plain objects and functions)23- Use this keyword24- Use for, while, or do-while loops25- Use push, pop, splice, or other mutating array methods26- Use delete operator on objects27- Use null (prefer undefined or Option types)2829## Immutable Update Patterns:30```typescript31const updateUser = (user: Readonly<User>, changes: Partial<User>): User =>32 ({ ...user, ...changes, updatedAt: new Date() });3334const removeItem = (items: readonly Item[], index: number): readonly Item[] =>35 [...items.slice(0, index), ...items.slice(index + 1)];3637const addItem = (items: readonly Item[], item: Item): readonly Item[] =>38 [...items, item];39```Common mistakes when making Cursor Follow a Specific Coding Style
Why it's a problem: Writing rules too broadly like 'use functional programming'
How to avoid: List every specific pattern: no for loops, no mutations, no classes, no this, no let/var. Include both forbidden and required patterns with examples.
Why it's a problem: Not providing utility functions for Cursor to import
How to avoid: Create src/lib/fp-utils.ts with your core utilities and reference it with @file in prompts.
Why it's a problem: Applying FP rules to files that need imperative patterns
How to avoid: 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
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Refactor this JavaScript module from imperative style to functional programming. Replace all for loops with map/filter/reduce, remove all mutations, convert classes to pure functions, and use a pipe function for data transformation chains. Keep behavior identical.
@functional-style.mdc @src/lib/fp-utils.ts Rewrite this module using functional programming patterns. Replace loops with Array methods, mutations with spread operators, and classes with pure functions. Use pipe from @/lib/fp-utils for transformation chains. All functions must be pure.
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.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation