RuleStack

Configs

Stacks

Compare

Diff

RuleStack

Configs

Stacks

Compare

Diff

Read API

RuleStack

Configs

Stacks

Compare

Diff

Read API

Configs/Cursor rules/VictorGoic0/Math-Tutoring-App

Cursor rule

.cursor/rules/react-readability.mdc

React code readability pass — apply after generating any React component

Cursor rules

Quality

57/100

Scores the file, not the repository.

Length

1,396 words

10 headings · 5 code blocks

Repository

0

— · pushed 112 days ago

Last changed

3 days ago

First indexed 3 days ago.
VictorGoic0/Math-Tutoring-App/.cursor/rules/react-readability.mdcRawGitHub
1---
2alwaysApply: false
3description: React code readability pass — apply after generating any React component
4---
5 
6# React readability pass
7 
8After generating or modifying any React component, perform a dedicated readability
9pass before considering the task complete. Do not combine this with implementation.
10The sole job of this pass is to make the code easy to read at a glance — not to
11change behaviour, not to refactor logic, not to add features.
12 
13---
14 
15## Principles
16 
17**Code is written for the next reader, not the machine.**
18A component should read top to bottom like a short document. Someone who has never
19seen this file should be able to understand what it does within thirty seconds.
20 
21**Names express intent, not implementation.**
22A name should tell you what a thing *means* in the context of this component, not
23what it technically is. `likesCount` over `likes`. `previousCommentsState` over
24`prev`. `isLiked` over `liked`. If you have to read the surrounding lines to
25understand a name, the name is wrong.
26 
27**Whitespace is punctuation.**
28A blank line means "these two groups of things are unrelated." No blank line means
29"these things belong together." Blank lines are not decorative — use them only where
30the grouping boundary is real. Never use more than one blank line between groups.
31 
32**Handlers and helpers are different things. Name and group them accordingly.**
33A handler responds to a user gesture and owns the try/catch, the toast, the
34side-effect. A helper mutates state on behalf of a handler or a child component —
35it is an imperative command, not an event response. They serve different purposes
36and belong in separate groups.
37 
38**Abstraction is earned, not assumed.**
39Do not extract a function because it is more than N lines. Extract it when the
40extraction has a name that is more expressive than the code it replaces, and when
41that name makes the call site read more clearly. Otherwise, leave it inline.
42 
43---
44 
45## Component template
46 
47Use this ordering for every component. Do not reorder sections.
48Separate each section with exactly one blank line.
49 
50```tsx
51// ─── 1. Props destructuring ───────────────────────────────────────────────────
52// Destructure immediately. Never access props.x anywhere below this block.
53// Name every destructured value for what it means in this component, not what
54// the parent called it if those differ.
55 
56const {
57 thumbnailUrl,
58 username,
59 id,
60} = props.post;
61 
62// ─── 2. State ─────────────────────────────────────────────────────────────────
63// All useState calls together. Name state variables for the thing they hold,
64// not for their setter. Context that the component "lives on" goes here too —
65// it is incoming data, same category as state.
66 
67const [likesCount, setLikesCount] = useState(Number(likes));
68const [postComments, setPostComments] = useState(comments);
69const [currentUser] = useContext(CurrentUserContext);
70 
71// ─── 3. Derived values ────────────────────────────────────────────────────────
72// Values computed from state or props. useMemo if expensive, plain const if not.
73// Never derive inside JSX — give the value a name here so the return stays clean.
74 
75const isPostOwner = currentUser?.username === username;
76const hasComments = postComments.length > 0;
77 
78// ─── 4. Effects ───────────────────────────────────────────────────────────────
79// useEffect calls. One blank line between each effect if there are multiple.
80// If an effect needs a comment to explain why it exists, write the comment.
81 
82useEffect(() => {
83 // reason this effect exists, if non-obvious
84}, [dependency]);
85 
86// ─── 5. Handlers ──────────────────────────────────────────────────────────────
87// Functions that respond directly to user gestures (clicks, submits, changes).
88// Naming convention: onVerb + Noun + Action — e.g. onClickLikePost, onSubmitLoginForm.
89// Handlers own the try/catch, the toast, and any async side-effect.
90// One blank line between each handler.
91 
92const onClickDeletePost = async () => {
93 try {
94 const response = await deletePost(id);
95 if (response) {
96 toast.success("Post deleted!");
97 }
98 } catch (error) {
99 console.error(error);
100 toast.error("Failed to delete post. Please try again.");
101 }
102};
103 
104const onClickLikePost = async () => {
105 const likePayload = {
106 user_id: currentUser.userID,
107 post_id: id,
108 };
109 try {
110 const response = await likePost(likePayload);
111 if (response) {
112 const isLiked = response.liked;
113 if (isLiked) {
114 setLikesCount((previousLikesCount) => previousLikesCount + 1);
115 toast.success("Post liked!");
116 } else {
117 setLikesCount((previousLikesCount) => previousLikesCount - 1);
118 toast.success("Post like removed :(");
119 }
120 }
121 } catch (error) {
122 console.error(error);
123 toast.error("Failed to like or remove like from post. Please try again.");
124 }
125};
126 
127// ─── 6. Helpers ───────────────────────────────────────────────────────────────
128// Imperative state-mutation functions. Not event responses — these are commands
129// called by handlers above or passed down to child components as props.
130// Naming convention: verb + Noun — e.g. addCommentToPost, removeCommentFromPost.
131// No try/catch here. No toasts. No side-effects. Pure state transitions only.
132 
133const addCommentToPost = (comment) => {
134 setPostComments((previousCommentsState) => [...previousCommentsState, comment]);
135};
136 
137const removeCommentFromPost = (commentId) => {
138 setPostComments((previousCommentsState) =>
139 previousCommentsState.filter((comment) => comment.id !== commentId)
140 );
141};
142 
143const addShowMoreCommentsToPost = (newComments) => {
144 setPostComments(newComments);
145};
146 
147// ─── 7. Return ────────────────────────────────────────────────────────────────
148// One blank line before the return. JSX should be as flat as the design allows.
149// No inline logic that needs a comment to understand — derive it in section 3.
150// No anonymous functions in JSX — all handlers and helpers are named above.
151 
152return (
153 <div className="post">
154 ...
155 </div>
156);
157```
158 
159---
160 
161## Naming reference
162 
163| Thing | Convention | Example |
164|---|---|---|
165| Event handler | `onVerb + Noun + Context` | `onClickLikePost`, `onSubmitLoginForm`, `onChangeSearchInput` |
166| State helper / child prop | `verb + Noun` | `addCommentToPost`, `removeCommentFromPost`, `resetFormFields` |
167| State variable | the thing it holds | `likesCount`, `postComments`, `isMenuOpen` |
168| setState callback arg | `previous + StateName` | `previousLikesCount`, `previousCommentsState` |
169| Boolean | reads as a question | `isLiked`, `hasComments`, `isPostOwner`, `canDelete` |
170| Async response | `response` (always) | `const response = await likePost(payload)` |
171| Extracted value from response | name what it means | `const isLiked = response.liked` |
172| Payload / request body | `nounPayload` | `likePayload`, `commentPayload` |
173 
174---
175 
176## Before / after examples
177 
178### Variable names
179 
180```tsx
181// Bad
182const res = await fetch(url);
183const data = await res.json();
184const filtered = data.filter(x => x.active);
185const [val, setVal] = useState(0);
186 
187// Good
188const response = await fetch(userProfileUrl);
189const userProfiles = await response.json();
190const activeProfiles = userProfiles.filter((profile) => profile.isActive);
191const [likesCount, setLikesCount] = useState(0);
192```
193 
194### setState callback argument
195 
196```tsx
197// Bad — "prev" tells you nothing about what the state is
198setLikesCount((prev) => prev + 1);
199setPostComments((prev) => [...prev, comment]);
200 
201// Good — the argument name matches the state variable name
202setLikesCount((previousLikesCount) => previousLikesCount + 1);
203setPostComments((previousCommentsState) => [...previousCommentsState, comment]);
204```
205 
206### Handler vs helper distinction
207 
208```tsx
209// Bad — a handler that does no error handling, and a "helper" that fires a toast
210const likePost = async () => {
211 await likePostApi(id); // no try/catch, no feedback
212};
213 
214const updateComments = (comment) => {
215 setPostComments([...postComments, comment]);
216 toast.success("Comment added!"); // toast does not belong in a helper
217};
218 
219// Good — handler owns the side-effect and feedback, helper is a pure state transition
220const onClickLikePost = async () => {
221 try {
222 const response = await likePost(id);
223 toast.success("Post liked!");
224 } catch (error) {
225 toast.error("Failed to like post. Please try again.");
226 }
227};
228 
229const addCommentToPost = (comment) => {
230 setPostComments((previousCommentsState) => [...previousCommentsState, comment]);
231};
232```
233 
234### Whitespace grouping
235 
236```tsx
237// Bad — no visual separation, everything runs together
238const [isOpen, setIsOpen] = useState(false);
239const title = props.title;
240useEffect(() => { document.title = title; }, [title]);
241const onClickOpen = () => setIsOpen(true);
242const filteredItems = items.filter(i => i.active);
243 
244// Good — each group is visually distinct, reading order matches mental model
245const { title, items } = props;
246 
247const [isOpen, setIsOpen] = useState(false);
248 
249const filteredItems = items.filter((item) => item.isActive);
250 
251useEffect(() => {
252 document.title = title;
253}, [title]);
254 
255const onClickOpen = () => setIsOpen(true);
256```
257 
258---
259 
260## Readability pass checklist
261 
262Run through this after every component generation before marking the task done:
263 
264- [ ] Props are destructured at the top in a single block
265- [ ] All state and context declarations are grouped together
266- [ ] Derived values are named and computed before the return — none inside JSX
267- [ ] Effects follow derived values
268- [ ] Handlers are named `onVerb + Noun` and own their own try/catch and toasts
269- [ ] Helpers are named `verb + Noun`, contain no toasts, no try/catch, no side-effects
270- [ ] setState callbacks name the previous state argument (`previousXxx`)
271- [ ] Booleans read as questions (`isX`, `hasX`, `canX`)
272- [ ] One blank line between sections, no double blank lines
273- [ ] No anonymous functions passed directly in JSX — all are named above
274- [ ] No name requires reading surrounding code to understand
275 

Sections

  • React readability pass
  • Principles
  • Component template
  • Naming reference
  • Before / after examples
  • Variable names
  • setState callback argument
  • Handler vs helper distinction
  • Whitespace grouping
  • Readability pass checklist

What it covers

code-styleuido-not

Stack — with the evidence

typescript

(1.00)

node

(0.70)

react

(0.70)

express

(0.70)

vite

(0.70)

javascript

(0.50)

Format

Cursor rules

The most expressive format here. Many small .mdc files, each with frontmatter declaring when it should load, so a rule about migrations only enters context when a migration is open. Costs the most to maintain and only one editor reads it.

What the corpus says about it

Repository

Owner
VictorGoic0
Language
—
License
—
Archived
no

All configs in this repo

Also in VictorGoic0/Math-Tutoring-App

Diff this repo’s formats

One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?

The other instruction files in this repository
RepositoryFormatStackCoversScoreChanged
VictorGoic0/Math-Tutoring-App.cursor/rules/api-patterns.mdc · 0Cursor rulestypescriptnode+4setupstylearchsecurity+273/1003 days ago
VictorGoic0/Math-Tutoring-App.cursor/rules/linting.mdc · 0Cursor rulestypescriptnode+4lint-formatstyledo-notagent-behaviour68/1003 days ago
VictorGoic0/Math-Tutoring-App.cursor/rules/one-pr-at-a-time.mdc · 0Cursor rulestypescriptnode+4git16/1003 days ago
VictorGoic0/Math-Tutoring-App.cursor/rules/react-patterns.mdc · 0Cursor rulestypescriptnode+4stylearchdo-not69/1003 days ago
Diff against .cursor/rules/api-patterns.mdc Diff against .cursor/rules/linting.mdc Diff against .cursor/rules/one-pr-at-a-time.mdc Diff against .cursor/rules/react-patterns.mdc

Similar configs

Same format, overlapping stack, ranked by quality.

Same format, overlapping stack, ranked by quality
RepositoryFormatStackCoversScoreChanged
hiromaily/go-crypto-wallet.cursor/rules/typescript.mdc · 126Cursor rulesgobun+5setupbuildtestlint-format+6100/1003 days ago
TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 45Cursor rulestypescriptpytest+15testlint-formatstylearch+5100/1003 days ago
markstev/mark-starter.cursor/rules/frontend.mdc · 0Cursor rulestypescriptturborepo+14setuptestlint-formatstyle+699/1003 days ago
Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0Cursor rulestypescriptturborepo+13setuptestlint-formatstyle+799/1003 days ago
dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0Cursor rulestypescriptturborepo+15setuptestlint-formatstyle+799/1003 days ago
deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1Cursor rulestypescriptnextjs+5setuptestlint-formatstyle+799/1003 days ago
langflow-ai/langflow.cursor/rules/docs_development.mdc · 153kCursor rulespythonnode+16setupbuildtestlint-format+797/1003 days ago
TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 45Cursor rulestypescriptpytest+15teststyletesting-strategysecurity+397/1003 days ago
RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack