| name | code |
| description | Use when writing TypeScript/React code - covers type safety, component patterns, and file organization |
Source Cursor rule: .cursor/rules/code.mdc.
Original Cursor alwaysApply: false.
Code Standards
TypeScript
No any, No Unsafe Casts
const TaskSchema = z.object({ id: z.string(), title: z.string() });
const task = TaskSchema.parse(response.data);
const parseResponse = (data: unknown): Task => {
if (!isTask(data)) throw new Error('Invalid');
return data;
};
const data: any = fetchData();
const task = response as Task;
const name = user!.name;
Generics Over Any
const first = <T>(items: T[]): T | undefined => items[0];
const first = (items: any[]): any => items[0];
React Patterns
Named Exports, PascalCase
export function TaskCard({ task }: TaskCardProps) { ... }
export default function taskCard() { ... }
Derive State, Avoid useEffect
const completedCount = tasks.filter(t => t.completed).length;
const [count, setCount] = useState(0);
useEffect(() => {
setCount(tasks.filter(t => t.completed).length);
}, [tasks]);
When useEffect IS Appropriate
useEffect(() => {
const sub = eventSource.subscribe(handler);
return () => sub.unsubscribe();
}, []);
useEffect(() => {
setHeight(ref.current?.getBoundingClientRect().height);
}, []);
Toasts with Sonner
import { toast } from 'sonner';
toast.success('Task created');
toast.error('Failed to save');
toast.promise(saveTask(), {
loading: 'Saving...',
success: 'Saved!',
error: 'Failed',
});
File Structure
Colocate at Route Level
app/(app)/[orgId]/tasks/
├── page.tsx # Server component
├── components/
│ └── TaskList.tsx # Client component
├── hooks/
│ └── useTasks.ts # SWR hook
└── data/
└── queries.ts # Server queries
Share Only When Reused 3+ Times
src/components/shared/ # Cross-page components
src/hooks/ # Shared hooks (useApiSWR, useDebounce)
Code Quality
File Size Limit: 300 Lines
Split large files into focused components.
Named Parameters for 2+ Args
const createTask = ({ title, assigneeId }: CreateTaskParams) => { ... };
createTask({ title: 'Review PR', assigneeId: user.id });
const createTask = (title: string, assigneeId: string) => { ... };
createTask('Review PR', user.id);
Early Returns
function processTask(task: Task | null) {
if (!task) return null;
if (task.deleted) return null;
return <TaskCard task={task} />;
}
function processTask(task) {
if (task) {
if (!task.deleted) {
return <TaskCard task={task} />;
}
}
return null;
}
Event Handler Naming
const handleClick = () => { ... };
const handleSubmit = (e: FormEvent) => { ... };
const handleTaskCreate = (task: Task) => { ... };
Accessibility
<div
role="button"
tabIndex={0}
onClick={handleClick}
onKeyDown={(e) => e.key === 'Enter' && handleClick()}
aria-label="Delete task"
>
<TrashIcon />
</div>
<label htmlFor="task-name">Task Name</label>
<input id="task-name" type="text" />