Guideline rules focused on applying the KISS (Keep It Simple, Stupid) principle to a TypeScript + React web-development stack.
You're spending more time wrestling with complex abstractions than building features. Your TypeScript interfaces have inheritance chains three levels deep. Your React components are doing twelve different things. Your utils folder has become a graveyard of "maybe we'll need this someday" functions.
It's time to embrace radical simplicity.
Every React developer has been there. You start with a simple feature, but then you think "what if we need to extend this?" So you add interfaces for extensibility. Then you create wrapper components for reusability. Before you know it, your simple user profile component has become a generic data-rendering framework that nobody understands.
The result? You spend 40% of your time navigating your own abstractions instead of shipping features. New team members take weeks to contribute meaningful code. Bug fixes that should take minutes require archaeological expeditions through your component hierarchy.
This isn't just about writing less code—it's about writing code that works with your brain, not against it.
These Cursor Rules implement the KISS principle (Keep It Simple, Stupid) specifically for TypeScript and React development. They guide your AI assistant to write code that solves today's problem with the smallest, clearest solution possible.
Instead of generating complex abstractions that might be useful someday, you get:
// What your AI generates WITH these rules
interface UserCardProps {
user: User;
onEdit: () => void;
}
function UserCard({ user, onEdit }: UserCardProps) {
return (
<div className="user-card">
<h3>{user.name}</h3>
<p>{user.email}</p>
<button onClick={onEdit}>Edit</button>
</div>
);
}
// Instead of this over-engineered mess
interface BaseCardProps<T> {
data: T;
renderer: CardRenderer<T>;
actions?: ActionConfig[];
}
class GenericCard<T> extends React.Component<BaseCardProps<T>> {
// 50 lines of abstraction for a simple card...
}
The rules ensure every AI-generated solution follows a strict hierarchy: solve the immediate problem first, add complexity only when profiling or user experience proves it's necessary.
status to the interface and rendering itWriting New Components: Your AI generates focused, single-purpose components instead of flexible-but-complex abstractions. A simple modal stays a simple modal until you actually need modal variants.
Debugging Issues: When something breaks, you're looking at 20 lines of clear logic instead of traversing a maze of generic wrappers and higher-order components.
Code Reviews: Team members understand changes immediately instead of spending 30 minutes deciphering your custom abstraction pattern.
Refactoring: Changes stay local. Need to modify how users are displayed? You touch the UserCard component, not a generic data renderer plus three configuration objects.
Copy the configuration into your .cursorrules file in your project root:
# In your project root
touch .cursorrules
# Paste the KISS-TS-React configuration
The rules work best with this minimal setup:
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"target": "ES2022",
"module": "ESNext"
}
}
// .eslintrc.js
module.exports = {
extends: ["@typescript-eslint/recommended"],
rules: {
"max-lines": ["error", 200],
"@typescript-eslint/no-explicit-any": "error"
}
};
Ask your AI to build features with phrases like:
The rules will guide it toward minimal, clear solutions.
Use prompts like:
Faster Feature Development: Teams report 40-60% faster feature delivery when components have single responsibilities and minimal interdependencies.
Reduced Bug Count: Simpler code has fewer places for bugs to hide. Less complexity means fewer edge cases and interaction bugs.
Faster Onboarding: New developers contribute meaningful code within days instead of weeks because they're not learning your custom abstraction framework.
Easier Maintenance: Changes stay local. Modifying user display logic doesn't require understanding your entire component architecture.
Better Test Coverage: Simple, focused functions are easy to test. You'll write more tests because testing becomes straightforward, not an exercise in mock orchestration.
Most development complexity comes from solving problems you don't have yet. These rules keep your AI focused on the problem in front of you, using the simplest solution that works.
When you actually need more complexity—when profiling shows a performance bottleneck, when you're truly building the same thing for the third time—then you add it. But not before.
Your codebase becomes a tool that amplifies your productivity instead of fighting it. Your team ships features instead of architecting abstractions. Your React apps become maintainable, understandable, and actually fun to work on.
The best code is code that solves problems clearly and gets out of your way. These rules ensure that's exactly what your AI assistant builds for you.
You are an expert in:
• TypeScript 5+
• React 18+ (functional components)
• Node.js (ESM build tooling)
• Jest / React-Testing-Library
Key Principles
- Embrace the KISS principle: solve today’s problem with the smallest, clearest solution.
- One purpose per unit: each file, function, and component should have a single responsibility.
- Favour readability over cleverness; code is read more often than written.
- Prefer composition over inheritance; small pieces combine more flexibly.
- Delay optimisation and advanced features until profiling or UX proves the need.
- Eliminate dead code and redundant abstractions during every refactor pass.
- Keep public APIs intentionally tiny; expose only what callers actually need.
TypeScript Rules
- Strict mode (`"strict": true`) is mandatory; it surfaces issues early without extra complexity.
- Use `interface` for public contracts, `type` for utility unions/aliases.
- Prefer explicit return types on exported functions/components.
- Arrow functions for one-liners; `function` keyword for multiline or hoisted utilities.
- Keep files ≤200 logical lines. Split once a file holds >2 top-level concepts.
- Naming
• Variables/constants: `camelCase` describing what, not how (`userToken`, `isLoading`).
• Components: `PascalCase` (`UserCard`).
• Files: `kebab-case` mirroring the default export (`user-card.tsx`).
- Remove unneeded generics—use them only when multiple concrete types are truly supported.
- Prefer `readonly` & `const` wherever mutation is unnecessary.
- Never `any`; when unsure, introduce a minimal interface or `unknown` + type-guard.
Error Handling and Validation
- Validate inputs at the top; bail early with descriptive errors.
```
function parseId(id: unknown): Id {
if (typeof id !== 'string') throw new TypeError('id must be string');
return id as Id;
}
```
- Keep try/catch blocks shallow—wrap only the statement that can throw.
- Centralise error formatting; UI components receive user-friendly messages, not raw Errors.
- Log technical details once (e.g., in a middleware) instead of scattering `console.error`.
- Avoid custom error hierarchies unless they simplify callers; prefer tagged messages over deep classes.
React Rules
- Functional components only; no class components.
- Each component renders a single visual concept; extract sub-components when JSX >40 lines.
- Local state via `useState`; elevate state only when 2+ distant branches need it.
- Favour derived data (`useMemo`) instead of storing duplicate state.
- Props must be typed with an explicit `Props` interface; no implicit `any`.
- Side effects live in `useEffect` with the smallest dependency array required.
- Styling hierarchy: CSS Modules → Tailwind → inline styles (last resort). Avoid multiple competing systems.
- Error boundaries wrap the route root, not every leaf.
Additional Sections
Testing
- Each pure util function gets a .test.ts file co-located.
- React components: test visible behaviour, not implementation details.
- Snapshots only for static, low-change components (e.g., icons).
- Keep tests simple: arrange-act-assert with minimal mocks.
Performance
- Measure before optimising; use React Profiler or Lighthouse.
- Lazy-load routes/components with `React.lazy` & `Suspense` when bundle size >250 kB.
- Avoid premature memoisation; add `memo`/`useMemo` only after a demonstrated bottleneck.
- Prefer native browser APIs (e.g., `IntersectionObserver`) over heavy polyfills.
Security
- Never interpolate user data directly into template literals that reach the DOM—always escape or use React’s default escaping.
- Store secrets only in environment variables; never commit them.
- Use HTTP-only, `SameSite=Lax` cookies for auth tokens; avoid localStorage.
Tooling
- ESLint + Prettier with the Airbnb base preset and `@typescript-eslint` plugin.
- Husky pre-commit: `npm run test -- --bail && eslint . --max-warnings 0`.
- Bundle with Vite for minimal config; configure aliasing in `tsconfig.paths` only when 2+ relative imports share the same root.
Common Pitfalls & How to Keep It Simple
- Resist “helper” classes that wrap a single function—inline or export the function instead.
- Do not introduce Redux/MobX until React Context + hooks prove insufficient.
- Prefer RESTful fetches with `fetch`/`axios`; add GraphQL only with multiple front-ends or complex querying needs.
- Avoid global "utils" folders; place helpers next to the domain they serve.
Project Structure (recommended)
```
src/
components/
button/
button.tsx
button.test.tsx
user-card/
user-card.tsx
hooks/
use-auth.ts
pages/
home.tsx
lib/
api.ts
types/
index.ts
```
These rules ensure every decision aligns with the KISS principle: smallest scope, clearest intent, minimal future guessing.