---
title: How I Stopped Cursor AI from Ruining My Code Style (and How You Can Too)
published: true
tags: cursor, ai, typescript, productivity
---
I've been using Cursor as my primary editor for about a month now — working full-time on a Next.js 14 project with TypeScript, Tailwind CSS, and a fairly opinionated component architecture.
The AI capabilities are genuinely impressive. But after the honeymoon phase, I started noticing a pattern that was quietly eating my productivity.
## The Problem Nobody Warns You About
The AI doesn't know your coding standards. And it doesn't care.
My project has clear conventions: functional components, named exports, Tailwind for all styling, strict TypeScript with zero tolerance for `any`, and early returns to keep nesting shallow.
But every time I asked Cursor to generate a component, help me refactor, or scaffold a new page, the output was a coin flip:
- Sometimes `export default function`, sometimes `export function` — no consistency
- Inline `style={{ color: 'red' }}` showing up in a Tailwind-only codebase
- `any` sprinkled around like confetti
- Pages Router patterns (`getServerSideProps`) sneaking into an App Router project
- Class components appearing in 2026
None of these are "wrong" in isolation. But in the context of my project, every single one creates friction. I was spending 20-30% of my time just reformatting AI output to match conventions.
That's not AI-assisted development. That's AI-generated tech debt.
## Discovering .cursorrules
I stumbled onto the solution while browsing GitHub repos one evening. Several open-source projects had a `.cursorrules` file sitting in their root directory — something I'd never noticed before.
The concept is simple: you create a plain text file called `.cursorrules` in your project root, write your coding standards in natural language, and Cursor's AI will respect them across all interactions — completions, chat, Composer, everything.
Think of it as onboarding documentation, but for your AI pair programmer instead of a new hire.
Here's a stripped-down example of what mine looks like:
```plaintext
You are a senior TypeScript developer working on a production Next.js application.
Tech Stack:
- TypeScript 5 with strict mode enabled
- React 18 with functional components only
- Next.js 14 using the App Router (NOT Pages Router)
- Tailwind CSS for all styling — no exceptions
Code Style Rules:
- Always use named exports. Never use default exports.
- Prefer early returns to reduce nesting. Max nesting depth: 2 levels.
- Never use the `any` type. Use proper interfaces, generics, or `unknown`.
- Use descriptive variable names. No single-letter variables except in short lambdas.
- Prefer `const` over `let`. Never use `var`.
- Always define proper TypeScript interfaces for component props.
File Structure:
- React components → /components
- Utility functions → /lib
- Type definitions → /types
- API routes → /app/api
```
After dropping this into my project root, the difference was immediate and dramatic. The AI-generated code went from "needs significant cleanup" to "basically ready to commit."
## Why Writing Good Rules Is Harder Than You Think
So I had the concept down. But getting the rules right? That took way more iteration than I expected.
**Problem 1: Too vague = useless**
My first attempt included gems like "write clean, maintainable code" and "follow best practices." The AI nodded politely and continued doing whatever it wanted. These instructions are the equivalent of telling a new developer to "just write good code" — technically correct, practically worthless.
**Problem 2: Too verbose = also useless**
My second attempt was a 150-line manifesto covering every edge case I could think of. The AI started ignoring chunks of it because there was too much to process. Rules files have an effective attention window, and flooding it with noise drowns out the signal.
**Problem 3: Stack-specific nuances are tricky**
The difference between "use React" and "use React 18 with functional components, hooks, and the App Router pattern" is enormous. The AI treats vague stack references as permission to use any version's patterns. I was getting `componentDidMount` mixed with `useEffect` in the same conversation.
**Problem 4: Phrasing matters more than you'd think**
"Don't use any" is weaker than "Never use the `any` type. For unknown types, use `unknown` and narrow with type guards." The more explicit and actionable the instruction, the better the compliance.
I spent probably 4-5 hours across two weeks refining my rules file. Effective? Yes. Fun? Not remotely.
## A Faster Path
Around iteration six of my hand-crafted rules file, a colleague pointed me to an online generator that takes a completely different approach: instead of writing rules in prose, you select your tech stack and preferences from a visual interface, and it outputs an optimized rules file.
The tool is at **ittoolshq.com/en/cursorrules-generator**
What made it click for me:
**Granular style controls.** It's not just "React yes/no." You can specify individual code style preferences — functional components, named exports, early returns, strict TypeScript — at a very fine-grained level. These are exactly the kinds of rules that make the biggest practical difference.
**Multi-editor support.** I use Cursor primarily, but I've been experimenting with Windsurf on a side project. This tool generates the right format for each:
- Cursor → `.cursorrules`
- Windsurf → `.windsurfrules`
- Cline → `.clinerules` or `AGENTS.md`
**Sensible defaults.** The generated output isn't just a list of "do this, don't do that." It structures the rules in a way that models respond well to — role framing, grouped directives, clear prohibitions.
The workflow is: open the page → check the boxes that match your stack → copy the output → paste into your project root → done. Took me about 2 minutes versus the hours I spent writing mine manually.
## The Before and After
Let me show you the actual difference in AI output quality.
**Before .cursorrules — asking Cursor to create a user profile card:**
```tsx
import React, { useState, useEffect } from 'react';
export default function UserProfile(props: any) {
const [userData, setUserData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(`/api/users/${props.id}`)
.then(res => res.json())
.then(data => {
setUserData(data);
setLoading(false);
});
}, [props.id]);
if (loading) {
return <div style={{ textAlign: 'center', padding: '20px' }}>Loading...</div>;
}
return (
<div style={{ border: '1px solid #ccc', borderRadius: '8px', padding: '16px' }}>
<img src={userData.avatar} style={{ width: '64px', borderRadius: '50%' }} />
<h2 style={{ margin: '8px 0' }}>{userData.name}</h2>
<p style={{ color: '#666' }}>{userData.email}</p>
</div>
);
}
```
Issues: default export, `any` props, inline styles everywhere, no TypeScript types, no error handling, no loading skeleton, untyped state.
**After .cursorrules — the exact same prompt:**
```tsx
import { useState, useEffect } from 'react'
interface UserProfileProps {
userId: string
}
interface UserData {
avatar: string
name: string
email: string
}
export function UserProfile({ userId }: UserProfileProps) {
const [user, setUser] = useState<UserData | null>(null)
const [error, setError] = useState<string | null>(null)
useEffect(() => {
const fetchUser = async () => {
try {
const res = await fetch(`/api/users/${userId}`)
if (!res.ok) throw new Error('Failed to fetch user')
const data: UserData = await res.json()
setUser(data)
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error')
}
}
fetchUser()
}, [userId])
if (error) return <div className="text-center p-5 text-red-500">{error}</div>
if (!user) return <UserProfileSkeleton />
return (
<div className="border border-gray-200 rounded-lg p-4">
<img
src={user.avatar}
alt={`${user.name}'s avatar`}
className="w-16 h-16 rounded-full"
/>
<h2 className="mt-2 text-lg font-semibold">{user.name}</h2>
<p className="text-gray-500">{user.email}</p>
</div>
)
}
```
Same AI. Same prompt. Same model. The only difference is a config file in the project root.
Named export. Proper interfaces. Tailwind classes. Early returns. Error handling. Typed state. Destructured props. No `any` in sight.
## The 5 Rules That Made the Biggest Impact
After weeks of experimentation, these are the specific rules that produced the most noticeable improvement in output quality:
### 1. "Never use `any`. Use `unknown` for truly unknown types and create proper interfaces for everything else."
This single rule eliminated roughly 90% of my type-safety fixes. Before this rule, the AI would default to `any` whenever the type wasn't immediately obvious. After — it actually takes the time to define interfaces.
### 2. "Use Next.js 14 App Router exclusively. Never use `getServerSideProps`, `getStaticProps`, or any Pages Router API."
Next.js has been around long enough that most training data includes Pages Router patterns. Without this explicit prohibition, the AI would regularly mix paradigms, which is especially confusing for newer developers on the team who might not recognize the mismatch.
### 3. "All styling must use Tailwind CSS utility classes. Never use inline styles, CSS modules, or styled-components."
This eliminated the most common source of inconsistency in generated code. The AI has strong tendencies toward inline styles (probably because they're self-contained and don't require imports), so you need to be very explicit about prohibiting them.
### 4. "Prefer early returns to reduce nesting. Maximum nesting depth is 2 levels."
This one surprised me with how well it works. The AI actually counts nesting levels and restructures logic with guard clauses. Code readability improved significantly.
### 5. "When creating new files, follow this directory structure: components in /components, utilities in /lib, type definitions in /types."
Subtle but powerful. When you ask the AI to create a new utility function, it'll suggest putting it in `/lib` instead of dropping it in a random location. Small thing, but it keeps the project organized as it grows.
## Practical Tips for Your Own Setup
A few things I've learned that might save you some trial and error:
**Start minimal.** Begin with 5-10 core rules that address your biggest pain points. You can always add more later. A focused rules file outperforms a comprehensive one.
**Be explicit about what NOT to do.** "Use Tailwind" is good. "Use Tailwind. Never use inline styles or CSS-in-JS" is better. The model responds more reliably to clear prohibitions.
**Include framework version numbers.** "React 18" and "Next.js 14 App Router" give the model much better context than just "React" and "Next.js."
**Update your rules as your project evolves.** I revisit mine roughly every two weeks. As new patterns emerge in the codebase, I add rules to codify them.
**Test with edge cases.** After updating your rules, ask the AI to generate something that would typically violate your conventions. If it still misbehaves, the rule needs to be rephrased or made more prominent.
## The ROI Calculation
Here's the math that convinced my team to adopt this across all our projects:
- Time to set up .cursorrules: **5 minutes** (with a generator) to **2 hours** (from scratch)
- Time saved per day on code reformatting: **15-30 minutes**
- Break-even point: **Day 1**
Over a month, that's roughly **8-10 hours** of developer time saved per person. For a team of four, that's a full work week recovered every month.
And the quality improvement is arguably even more valuable than the time savings. Fewer style inconsistencies means fewer code review comments, fewer merge conflicts from reformatting, and a more consistent codebase overall.
## Getting Started
If you want to try this yourself:
**Option A: Write your own.** Create a `.cursorrules` file in your project root. Start with your 5 most important conventions. Test, iterate, refine.
**Option B: Generate one.** Go to **ittoolshq.com/en/cursorrules-generator**, select your stack and preferences, copy the output. Customize from there.
Either way, the important thing is to have one. An imperfect `.cursorrules` file is infinitely better than no `.cursorrules` file.
If you're using Windsurf, the same concept applies — just name the file `.windsurfrules`. For Cline users, it's `.clinerules` or `AGENTS.md`.
---
What rules have you found most effective in your own setup? I'm always looking to refine mine — drop your best tips in the comments.