celeroncoder / celeroncoder/coco

React Review Audit

Open
#1 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
0
Forks
0
PR merge metrics
No merged PRs in 30d

Description

**Score: 76/100** · 4 errors · 74 warnings

Copy as prompt

```text
Fix the following React Review diagnostics in my codebase.

## Errors (4)

1. [error] rules-of-hooks — apps/web/app/threads/[id]/_components/RightSidebar.tsx:182
React Hook "useRef" is called conditionally. React Hooks must be called in the exact same order in every component render.

2. [error] rules-of-hooks — apps/web/app/threads/[id]/_components/RightSidebar.tsx:183
React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render.

3. [error] rules-of-hooks — apps/web/app/threads/[id]/_components/RightSidebar.tsx:184
React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render.

4. [error] rules-of-hooks — apps/web/app/threads/[id]/_components/RightSidebar.tsx:186
React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render.

## Warnings (74)

5. [warning] no-react19-deprecated-apis — apps/web/src/components/ui/animated-beam-logos.tsx:3
forwardRef is no longer needed on React 19+ — refs are regular props on function components; remove forwardRef and pass ref directly

6. [warning] nextjs-missing-metadata — apps/web/app/page.tsx:1
Page without metadata or generateMetadata export — hurts SEO

7. [warning] prefer-useReducer — apps/web/app/threads/[id]/thread-detail-client.tsx:39
Component "ThreadDetailClient" has 7 useState calls — consider useReducer for related state

8. [warning] no-giant-component — apps/web/app/threads/[id]/thread-detail-client.tsx:39
Component "ThreadDetailClient" is 345 lines — consider breaking it into smaller focused components

9. [warning] react-compiler-destructure-method — apps/web/app/threads/[id]/thread-detail-client.tsx:233
Destructure for clarity: `const { back } = useRouter()` then call `back(...)` directly — easier for React Compiler to memoize and clearer about which methods this component depends on

10. [warning] react-compiler-destructure-method — apps/web/app/threads/[id]/thread-detail-client.tsx:284
Destructure for clarity: `const { push } = useRouter()` then call `push(...)` directly — easier for React Compiler to memoize and clearer about which methods this component depends on

11. [warning] nextjs-missing-metadata — apps/web/app/devices/page.tsx:1
Page without metadata or generateMetadata export — hurts SEO

12. [warning] nextjs-missing-metadata — apps/web/app/threads/[id]/page.tsx:1
Page without metadata or generateMetadata export — hurts SEO

13. [warning] no-autofocus — apps/web/app/devices/devices-client.tsx:142
The `autoFocus` attribute is found here, which can cause usability issues for sighted and non-sighted users.

14. [warning] rendering-hydration-mismatch-time — apps/web/app/devices/devices-client.tsx:47
Date.now() reachable from JSX renders differently on server vs client — wrap in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional

15. [warning] rendering-hydration-mismatch-time — apps/web/app/devices/devices-client.tsx:47
Date.now() reachable from JSX renders differently on server vs client — wrap in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional

16. [warning] no-danger — apps/web/src/components/ai-elements/code-view.tsx:183
Do not use `dangerouslySetInnerHTML` prop

17. [warning] js-set-map-lookups — apps/web/src/components/ai-elements/code-view.tsx:295
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

18. [warning] js-combine-iterations — apps/web/src/components/ai-elements/code-view.tsx:307
.map().filter() iterates the array twice — combine into a single loop with .reduce() or for...of

19. [warning] no-long-transition-duration — apps/web/app/threads/[id]/_components/TodoPanel.tsx:151
1500ms transition is too slow for UI feedback — keep transitions under 1000ms. Use longer durations only for page-load hero animations

20. [warning] no-react19-deprecated-apis — apps/web/src/components/right-sidebar-context.tsx:3
useContext is superseded by `use()` on React 19+ — `use()` reads context conditionally inside hooks, branches, and loops; switch to `import { use } from 'react'`

21. [warning] prefer-use-effect-event — apps/web/app/threads/[id]/_components/RightSidebar.tsx:101
"onClose" is read only inside `addEventListener` — wrap it with useEffectEvent and remove it from the dep array so the effect doesn't re-synchronize on every parent render

22. [warning] no-derived-useState — apps/web/app/threads/[id]/_components/RightSidebar.tsx:89
useState initialized from prop "defaultTab" — if this value should stay in sync with the prop, derive it during render instead

23. [warning] no-effect-event-handler — apps/web/app/threads/[id]/_components/RightSidebar.tsx:91
useEffect simulating an event handler — move logic to an actual event handler instead

24. [warning] no-cascading-set-state — apps/web/app/threads/[id]/_components/RightSidebar.tsx:264
4 setState calls in a single useEffect — consider using useReducer or deriving state

25. [warning] design-no-three-period-ellipsis — apps/web/app/threads/[id]/_components/RightSidebar.tsx:403
Three-period ellipsis ("...") in JSX text — use the actual ellipsis character "…" (or `…`)

26. [warning] rendering-hydration-mismatch-time — apps/web/src/components/landing-page.tsx:94
new Date() reachable from JSX renders differently on server vs client — wrap in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional

27. [warning] prefer-useReducer — apps/web/app/workspaces/workspaces-client.tsx:137
Component "NewWorkspaceModal" has 5 useState calls — consider useReducer for related state

28. [warning] no-derived-useState — apps/web/app/workspaces/workspaces-client.tsx:235
useState initialized from prop "workspace" — if this value should stay in sync with the prop, derive it during render instead

29. [warning] no-derived-useState — apps/web/app/workspaces/workspaces-client.tsx:236
useState initialized from prop "workspace" — if this value should stay in sync with the prop, derive it during render instead

30. [warning] no-react19-deprecated-apis — apps/web/src/components/icons.tsx:90
forwardRef is no longer needed on React 19+ — refs are regular props on function components; remove forwardRef and pass ref directly

31. [warning] async-await-in-loop — apps/web/app/api/daemon/pair/poll/route.ts:15
await inside a while-loop runs the calls sequentially — for independent operations, collect them and use `await Promise.all(items.map(...))` to run them concurrently

32. [warning] nextjs-missing-metadata — apps/web/app/workspaces/page.tsx:1
Page without metadata or generateMetadata export — hurts SEO

33. [warning] no-array-index-as-key — apps/web/app/threads/[id]/_components/MessageBubble.tsx:110
Array index "i" used as key — causes bugs when list is reordered or filtered

34. [warning] no-array-index-as-key — apps/web/app/threads/[id]/_components/MessageBubble.tsx:117
Array index "i" used as key — causes bugs when list is reordered or filtered

35. [warning] js-flatmap-filter — apps/web/app/threads/[id]/_components/MessageBubble.tsx:172
.map().filter(Boolean) iterates twice — use .flatMap() to transform and filter in a single pass

36. [warning] rendering-hydration-mismatch-time — apps/web/app/threads/threads-client.tsx:50
new Date() reachable from JSX renders differently on server vs client — wrap in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional

37. [warning] rendering-hydration-mismatch-time — apps/web/app/threads/threads-client.tsx:50
new Date() reachable from JSX renders differently on server vs client — wrap in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional

38. [warning] rendering-hydration-mismatch-time — apps/web/app/threads/threads-client.tsx:50
new Date() reachable from JSX renders differently on server vs client — wrap in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional

39. [warning] js-batch-dom-css — apps/web/app/threads/[id]/_components/Composer.tsx:65
Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

40. [warning] no-prevent-default — apps/web/app/threads/[id]/_components/Composer.tsx:158
preventDefault() on onSubmit — form won't work without JavaScript. Consider using a server action for progressive enhancement

41. [warning] react-compiler-destructure-method — apps/web/src/components/auth-layout.tsx:56
Destructure for clarity: `const { push } = useRouter()` then call `push(...)` directly — easier for React Compiler to memoize and clearer about which methods this component depends on

42. [warning] no-generic-handler-names — apps/web/src/components/auth-layout.tsx:65
Non-descriptive handler name "handleClick" — name should describe what it does, not when it runs

43. [warning] nextjs-missing-metadata — apps/web/app/threads/page.tsx:1
Page without metadata or generateMetadata export — hurts SEO

44. [warning] nextjs-missing-metadata — apps/web/app/sign-up/[[...sign-up]]/page.tsx:1
Page without metadata or generateMetadata export — hurts SEO

45. [warning] label-has-associated-control — apps/web/src/components/app-shell/sidebar.tsx:400
A form label must be associated with a control.

46. [warning] label-has-associated-control — apps/web/src/components/app-shell/sidebar.tsx:494
A form label must be associated with a control.

47. [warning] label-has-associated-control — apps/web/src/components/app-shell/sidebar.tsx:510
A form label must be associated with a control.

48. [warning] label-has-associated-control — apps/web/src/components/app-shell/sidebar.tsx:521
A form label must be associated with a control.

49. [warning] prefer-useReducer — apps/web/src/components/app-shell/sidebar.tsx:43
Component "AppSidebar" has 5 useState calls — consider useReducer for related state

50. [warning] rerender-state-only-in-handlers — apps/web/src/components/app-shell/sidebar.tsx:51
useState "searchOpen" is updated but never read in the component's return — use useRef so updates don't trigger re-renders

51. [warning] js-combine-iterations — apps/web/src/components/app-shell/sidebar.tsx:77
.filter().map() iterates the array twice — combine into a single loop with .reduce() or for...of

52. [warning] no-effect-chain — apps/web/src/components/app-shell/sidebar.tsx:366
useEffect reacts to "wsId" which is set by another useEffect — chains of effects add an extra render per link and become rigid as code evolves. Compute what you can during render and write all related state inside the event handler that originally fires the chain

53. [warning] react-compiler-destructure-method — apps/web/src/components/app-shell/sidebar.tsx:385
Destructure for clarity: `const { push } = useRouter()` then call `push(...)` directly — easier for React Compiler to memoize and clearer about which methods this component depends on

54. [warning] no-cascading-set-state — apps/web/src/components/app-shell/sidebar.tsx:463
3 setState calls in a single useEffect — consider using useReducer or deriving state

55. [warning] no-effect-event-handler — apps/web/src/components/app-shell/sidebar.tsx:463
useEffect simulating an event handler — move logic to an actual event handler instead

56. [warning] design-no-em-dash-in-jsx-text — apps/web/src/components/ai-elements/tool-call.tsx:129
Em dash (—) in JSX text reads as model output — replace with comma, colon, semicolon, or parentheses

57. [warning] nextjs-missing-metadata — apps/web/app/sign-in/[[...sign-in]]/page.tsx:1
Page without metadata or generateMetadata export — hurts SEO

58. [warning] no-redundant-roles — apps/web/src/components/ai-elements/timeline.tsx:387
The `ol` element has an implicit role of `list`. Defining this explicitly is redundant and should be avoided.

59. [warning] use-lazy-motion — apps/web/src/components/ai-elements/timeline.tsx:3
Import "m" with LazyMotion instead of "motion" — saves ~30kb in bundle size

60. [warning] js-set-map-lookups — apps/web/src/components/ai-elements/timeline.tsx:461
array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

61. [warning] design-no-em-dash-in-jsx-text — apps/web/src/components/ai-elements/timeline.tsx:763
Em dash (—) in JSX text reads as model output — replace with comma, colon, semicolon, or parentheses

62. [warning] prefer-use-effect-event — apps/web/src/components/ai-elements/response.tsx:48
"onClose" is read only inside `addEventListener` — wrap it with useEffectEvent and remove it from the dep array so the effect doesn't re-synchronize on every parent render

63. [warning] no-effect-event-handler — apps/web/src/components/ai-elements/response.tsx:50
useEffect simulating an event handler — move logic to an actual event handler instead

64. [warning] no-danger — apps/web/src/components/ai-elements/grep-code-view.tsx:75
Do not use `dangerouslySetInnerHTML` prop

65. [warning] no-react19-deprecated-apis — apps/web/src/components/ui/matrix.tsx:422
forwardRef is no longer needed on React 19+ — refs are regular props on function components; remove forwardRef and pass ref directly

66. [warning] async-await-in-loop — apps/web/app/api/daemon/runs/pending/route.ts:19
await inside a while-loop runs the calls sequentially — for independent operations, collect them and use `await Promise.all(items.map(...))` to run them concurrently

67. [warning] no-react19-deprecated-apis — apps/web/src/components/ui/terminal.tsx:6
useContext is superseded by `use()` on React 19+ — `use()` reads context conditionally inside hooks, branches, and loops; switch to `import { use } from 'react'`

68. [warning] use-lazy-motion — apps/web/src/components/ui/terminal.tsx:14
Import "m" with LazyMotion instead of "motion" — saves ~30kb in bundle size

69. [warning] rerender-state-only-in-handlers — apps/web/src/components/ui/terminal.tsx:140
useState "started" is updated but never read in the component's return — use useRef so updates don't trigger re-renders

70. [warning] no-cascading-set-state — apps/web/src/components/ui/terminal.tsx:162
3 setState calls in a single useEffect — consider using useReducer or deriving state

71. [warning] no-array-index-as-key — apps/web/src/components/ui/terminal.tsx:266
Array index "index" used as key — causes bugs when list is reordered or filtered

72. [warning] design-no-redundant-size-axes — apps/web/src/components/ui/terminal.tsx:282
w-2 h-2 → use the shorthand size-2 (Tailwind v3.4+)

73. [warning] design-no-redundant-size-axes — apps/web/src/components/ui/terminal.tsx:283
w-2 h-2 → use the shorthand size-2 (Tailwind v3.4+)

74. [warning] design-no-redundant-size-axes — apps/web/src/components/ui/terminal.tsx:284
w-2 h-2 → use the shorthand size-2 (Tailwind v3.4+)

75. [warning] no-react19-deprecated-apis — apps/web/src/components/ui/sidebar.tsx:48
useContext is superseded by `use()` on React 19+ — `use()` reads context conditionally inside hooks, branches, and loops; switch to `import { use } from 'react'`

76. [warning] no-derived-useState — apps/web/src/components/ui/sidebar.tsx:74
useState initialized from prop "defaultOpen" — if this value should stay in sync with the prop, derive it during render instead

77. [warning] no-inline-exhaustive-style — apps/web/src/lib/dotmatrix-core.tsx:784
8 inline style properties — extract to a CSS class, CSS module, or styled component for maintainability and reuse

78. [warning] use-lazy-motion — apps/web/src/components/ui/animated-beam.tsx:3
Import "m" with LazyMotion instead of "motion" — saves ~30kb in bundle size
```

---

### ❌ Errors (4)

**`rules-of-hooks`**

React Hook "useRef" is called conditionally. React Hooks must be called in the exact same order in every component render.

> Move the Hook call before the condition, or call it unconditionally and branch inside the Hook/effect instead.

[`apps/web/app/threads/[id]/_components/RightSidebar.tsx:182`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/_components/RightSidebar.tsx#L182)
[`apps/web/app/threads/[id]/_components/RightSidebar.tsx:183`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/_components/RightSidebar.tsx#L183)
[`apps/web/app/threads/[id]/_components/RightSidebar.tsx:184`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/_components/RightSidebar.tsx#L184)
[`apps/web/app/threads/[id]/_components/RightSidebar.tsx:186`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/_components/RightSidebar.tsx#L186)

---

### ⚠️ Warnings (74)

**`nextjs-missing-metadata`**

Page without metadata or generateMetadata export — hurts SEO

> Add `export const metadata = { title: '...', description: '...' }` or `export async function generateMetadata()`

[`apps/web/app/page.tsx:1`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/page.tsx#L1)
[`apps/web/app/devices/page.tsx:1`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/devices/page.tsx#L1)
[`apps/web/app/threads/[id]/page.tsx:1`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/page.tsx#L1)
[`apps/web/app/workspaces/page.tsx:1`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/workspaces/page.tsx#L1)
[`apps/web/app/threads/page.tsx:1`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/page.tsx#L1)
[`apps/web/app/sign-up/[[...sign-up]]/page.tsx:1`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/sign-up/[[...sign-up]]/page.tsx#L1)
[`apps/web/app/sign-in/[[...sign-in]]/page.tsx:1`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/sign-in/[[...sign-in]]/page.tsx#L1)

**`no-react19-deprecated-apis`**

forwardRef is no longer needed on React 19+ — refs are regular props on function components; remove forwardRef and pass ref directly

> Pass `ref` as a regular prop on function components — `forwardRef` is no longer needed in React 19+. Replace `useContext(X)` with `use(X)` for branch-aware context reads. Only enabled on projects detected as React 19+.

[`apps/web/src/components/ui/animated-beam-logos.tsx:3`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ui/animated-beam-logos.tsx#L3)
[`apps/web/src/components/right-sidebar-context.tsx:3`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/right-sidebar-context.tsx#L3)
[`apps/web/src/components/icons.tsx:90`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/icons.tsx#L90)
[`apps/web/src/components/ui/matrix.tsx:422`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ui/matrix.tsx#L422)
[`apps/web/src/components/ui/terminal.tsx:6`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ui/terminal.tsx#L6)
[`apps/web/src/components/ui/sidebar.tsx:48`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ui/sidebar.tsx#L48)

**`rendering-hydration-mismatch-time`**

Date.now() reachable from JSX renders differently on server vs client — wrap in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional

> Wrap dynamic time/random values in useEffect+useState (client-only) or add suppressHydrationWarning to the parent if intentional

[`apps/web/app/devices/devices-client.tsx:47`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/devices/devices-client.tsx#L47)
[`apps/web/app/devices/devices-client.tsx:47`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/devices/devices-client.tsx#L47)
[`apps/web/src/components/landing-page.tsx:94`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/landing-page.tsx#L94)
[`apps/web/app/threads/threads-client.tsx:50`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/threads-client.tsx#L50)
[`apps/web/app/threads/threads-client.tsx:50`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/threads-client.tsx#L50)
[`apps/web/app/threads/threads-client.tsx:50`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/threads-client.tsx#L50)

**`react-compiler-destructure-method`**

Destructure for clarity: `const { back } = useRouter()` then call `back(...)` directly — easier for React Compiler to memoize and clearer about which methods this component depends on

> Destructure the method up front: `const { push } = useRouter()` then call `push(...)` directly — clearer dependency graph and easier for React Compiler to memoize

[`apps/web/app/threads/[id]/thread-detail-client.tsx:233`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/thread-detail-client.tsx#L233)
[`apps/web/app/threads/[id]/thread-detail-client.tsx:284`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/thread-detail-client.tsx#L284)
[`apps/web/src/components/auth-layout.tsx:56`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/auth-layout.tsx#L56)
[`apps/web/src/components/app-shell/sidebar.tsx:385`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/app-shell/sidebar.tsx#L385)

**`no-derived-useState`**

useState initialized from prop "defaultTab" — if this value should stay in sync with the prop, derive it during render instead

> Remove useState and compute the value inline: `const value = transform(propName)`

[`apps/web/app/threads/[id]/_components/RightSidebar.tsx:89`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/_components/RightSidebar.tsx#L89)
[`apps/web/app/workspaces/workspaces-client.tsx:235`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/workspaces/workspaces-client.tsx#L235)
[`apps/web/app/workspaces/workspaces-client.tsx:236`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/workspaces/workspaces-client.tsx#L236)
[`apps/web/src/components/ui/sidebar.tsx:74`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ui/sidebar.tsx#L74)

**`label-has-associated-control`**

A form label must be associated with a control.

> Either give the label a `htmlFor` attribute with the id of the associated control, or wrap the label around the control.

[`apps/web/src/components/app-shell/sidebar.tsx:400`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/app-shell/sidebar.tsx#L400)
[`apps/web/src/components/app-shell/sidebar.tsx:494`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/app-shell/sidebar.tsx#L494)
[`apps/web/src/components/app-shell/sidebar.tsx:510`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/app-shell/sidebar.tsx#L510)
[`apps/web/src/components/app-shell/sidebar.tsx:521`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/app-shell/sidebar.tsx#L521)

**`prefer-useReducer`**

Component "ThreadDetailClient" has 7 useState calls — consider useReducer for related state

> Group related state: `const [state, dispatch] = useReducer(reducer, { field1, field2, ... })`

[`apps/web/app/threads/[id]/thread-detail-client.tsx:39`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/thread-detail-client.tsx#L39)
[`apps/web/app/workspaces/workspaces-client.tsx:137`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/workspaces/workspaces-client.tsx#L137)
[`apps/web/src/components/app-shell/sidebar.tsx:43`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/app-shell/sidebar.tsx#L43)

**`no-effect-event-handler`**

useEffect simulating an event handler — move logic to an actual event handler instead

> Move the conditional logic into onClick, onChange, or onSubmit handlers directly

[`apps/web/app/threads/[id]/_components/RightSidebar.tsx:91`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/_components/RightSidebar.tsx#L91)
[`apps/web/src/components/app-shell/sidebar.tsx:463`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/app-shell/sidebar.tsx#L463)
[`apps/web/src/components/ai-elements/response.tsx:50`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ai-elements/response.tsx#L50)

**`no-cascading-set-state`**

4 setState calls in a single useEffect — consider using useReducer or deriving state

> Combine into useReducer: `const [state, dispatch] = useReducer(reducer, initialState)`

[`apps/web/app/threads/[id]/_components/RightSidebar.tsx:264`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/_components/RightSidebar.tsx#L264)
[`apps/web/src/components/app-shell/sidebar.tsx:463`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/app-shell/sidebar.tsx#L463)
[`apps/web/src/components/ui/terminal.tsx:162`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ui/terminal.tsx#L162)

**`no-array-index-as-key`**

Array index "i" used as key — causes bugs when list is reordered or filtered

> Use a stable unique identifier: `key={item.id}` or `key={item.slug}` — index keys break on reorder/filter

[`apps/web/app/threads/[id]/_components/MessageBubble.tsx:110`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/_components/MessageBubble.tsx#L110)
[`apps/web/app/threads/[id]/_components/MessageBubble.tsx:117`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/_components/MessageBubble.tsx#L117)
[`apps/web/src/components/ui/terminal.tsx:266`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ui/terminal.tsx#L266)

**`use-lazy-motion`**

Import "m" with LazyMotion instead of "motion" — saves ~30kb in bundle size

> Use `import { LazyMotion, m } from "framer-motion"` with `domAnimation` features — saves ~30kb

[`apps/web/src/components/ai-elements/timeline.tsx:3`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ai-elements/timeline.tsx#L3)
[`apps/web/src/components/ui/terminal.tsx:14`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ui/terminal.tsx#L14)
[`apps/web/src/components/ui/animated-beam.tsx:3`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ui/animated-beam.tsx#L3)

**`design-no-redundant-size-axes`**

w-2 h-2 → use the shorthand size-2 (Tailwind v3.4+)

> Collapse `w-N h-N` to `size-N` (Tailwind v3.4+) when both axes match

[`apps/web/src/components/ui/terminal.tsx:282`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ui/terminal.tsx#L282)
[`apps/web/src/components/ui/terminal.tsx:283`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ui/terminal.tsx#L283)
[`apps/web/src/components/ui/terminal.tsx:284`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ui/terminal.tsx#L284)

**`no-danger`**

Do not use `dangerouslySetInnerHTML` prop

> `dangerouslySetInnerHTML` is a way to inject HTML into your React component. This is dangerous because it can easily lead to XSS vulnerabilities.

[`apps/web/src/components/ai-elements/code-view.tsx:183`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ai-elements/code-view.tsx#L183)
[`apps/web/src/components/ai-elements/grep-code-view.tsx:75`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ai-elements/grep-code-view.tsx#L75)

**`js-set-map-lookups`**

array.includes() in a loop is O(n) per call — convert to a Set for O(1) lookups

> Use a `Set` or `Map` for repeated membership tests / keyed lookups — `Array.includes`/`find` is O(n) per call

[`apps/web/src/components/ai-elements/code-view.tsx:295`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ai-elements/code-view.tsx#L295)
[`apps/web/src/components/ai-elements/timeline.tsx:461`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ai-elements/timeline.tsx#L461)

**`js-combine-iterations`**

.map().filter() iterates the array twice — combine into a single loop with .reduce() or for...of

> Combine `.map().filter()` (or similar chains) into a single pass with `.reduce()` or a `for...of` loop to avoid iterating the array twice

[`apps/web/src/components/ai-elements/code-view.tsx:307`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ai-elements/code-view.tsx#L307)
[`apps/web/src/components/app-shell/sidebar.tsx:77`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/app-shell/sidebar.tsx#L77)

**`prefer-use-effect-event`**

"onClose" is read only inside `addEventListener` — wrap it with useEffectEvent and remove it from the dep array so the effect doesn't re-synchronize on every parent render

> Wrap the callback with `useEffectEvent(callback)` (React 19+) and call the resulting binding from inside the sub-handler. The Effect Event captures the latest props/state without being a reactive dep, so the effect doesn't re-subscribe on every parent render. See https://react.dev/reference/react/useEffectEvent

[`apps/web/app/threads/[id]/_components/RightSidebar.tsx:101`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/_components/RightSidebar.tsx#L101)
[`apps/web/src/components/ai-elements/response.tsx:48`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ai-elements/response.tsx#L48)

**`async-await-in-loop`**

await inside a while-loop runs the calls sequentially — for independent operations, collect them and use `await Promise.all(items.map(...))` to run them concurrently

> Collect the items and use `await Promise.all(items.map(...))` to run independent operations concurrently

[`apps/web/app/api/daemon/pair/poll/route.ts:15`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/api/daemon/pair/poll/route.ts#L15)
[`apps/web/app/api/daemon/runs/pending/route.ts:19`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/api/daemon/runs/pending/route.ts#L19)

**`rerender-state-only-in-handlers`**

useState "searchOpen" is updated but never read in the component's return — use useRef so updates don't trigger re-renders

> Replace useState with useRef when the value is only mutated and never read in render — `ref.current = ...` updates without re-rendering the component

[`apps/web/src/components/app-shell/sidebar.tsx:51`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/app-shell/sidebar.tsx#L51)
[`apps/web/src/components/ui/terminal.tsx:140`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ui/terminal.tsx#L140)

**`design-no-em-dash-in-jsx-text`**

Em dash (—) in JSX text reads as model output — replace with comma, colon, semicolon, or parentheses

> Replace em dashes in JSX text with commas, colons, semicolons, periods, or parentheses — em dashes read as model-output filler

[`apps/web/src/components/ai-elements/tool-call.tsx:129`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ai-elements/tool-call.tsx#L129)
[`apps/web/src/components/ai-elements/timeline.tsx:763`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ai-elements/timeline.tsx#L763)

**`no-giant-component`**

Component "ThreadDetailClient" is 345 lines — consider breaking it into smaller focused components

> Extract logical sections into focused components: `<UserHeader />`, `<UserActions />`, etc.

[`apps/web/app/threads/[id]/thread-detail-client.tsx:39`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/thread-detail-client.tsx#L39)

**`no-autofocus`**

The `autoFocus` attribute is found here, which can cause usability issues for sighted and non-sighted users.

> Remove the `autoFocus` attribute.

[`apps/web/app/devices/devices-client.tsx:142`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/devices/devices-client.tsx#L142)

**`no-long-transition-duration`**

1500ms transition is too slow for UI feedback — keep transitions under 1000ms. Use longer durations only for page-load hero animations

> Keep UI transitions under 1s — 100-150ms for instant feedback, 200-300ms for state changes, 300-500ms for layout changes. Use longer durations only for page-load hero animations

[`apps/web/app/threads/[id]/_components/TodoPanel.tsx:151`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/_components/TodoPanel.tsx#L151)

**`design-no-three-period-ellipsis`**

Three-period ellipsis ("...") in JSX text — use the actual ellipsis character "…" (or `…`)

> Use the typographic ellipsis "…" (or `…`) instead of three periods — pairs with action-with-followup labels ("Rename…", "Loading…")

[`apps/web/app/threads/[id]/_components/RightSidebar.tsx:403`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/_components/RightSidebar.tsx#L403)

**`js-flatmap-filter`**

.map().filter(Boolean) iterates twice — use .flatMap() to transform and filter in a single pass

> Use `.flatMap(item => condition ? [value] : [])` — transforms and filters in a single pass instead of creating an intermediate array

[`apps/web/app/threads/[id]/_components/MessageBubble.tsx:172`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/_components/MessageBubble.tsx#L172)

**`js-batch-dom-css`**

Multiple sequential element.style assignments — batch with cssText or classList for fewer reflows

> Batch DOM/CSS reads and writes — interleaving them inside a loop causes layout thrashing. Read first, then write

[`apps/web/app/threads/[id]/_components/Composer.tsx:65`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/_components/Composer.tsx#L65)

**`no-prevent-default`**

preventDefault() on <form> onSubmit — form won't work without JavaScript. Consider using a server action for progressive enhancement

> Use `<form action={serverAction}>` (works without JS) or `<button>` instead of `<a>` with preventDefault

[`apps/web/app/threads/[id]/_components/Composer.tsx:158`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/app/threads/[id]/_components/Composer.tsx#L158)

**`no-generic-handler-names`**

Non-descriptive handler name "handleClick" — name should describe what it does, not when it runs

> Rename to describe the action: e.g. `handleSubmit` → `saveUserProfile`, `handleClick` → `toggleSidebar`

[`apps/web/src/components/auth-layout.tsx:65`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/auth-layout.tsx#L65)

**`no-effect-chain`**

useEffect reacts to "wsId" which is set by another useEffect — chains of effects add an extra render per link and become rigid as code evolves. Compute what you can during render and write all related state inside the event handler that originally fires the chain

> Compute as much as possible during render (e.g. `const isGameOver = round > 5`) and write all related state inside the event handler that originally fires the chain. Each effect link adds an extra render and makes the code rigid as requirements evolve

[`apps/web/src/components/app-shell/sidebar.tsx:366`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/app-shell/sidebar.tsx#L366)

**`no-redundant-roles`**

The `ol` element has an implicit role of `list`. Defining this explicitly is redundant and should be avoided.

> Remove the redundant role `list` from the element `ol`.

[`apps/web/src/components/ai-elements/timeline.tsx:387`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/components/ai-elements/timeline.tsx#L387)

**`no-inline-exhaustive-style`**

8 inline style properties — extract to a CSS class, CSS module, or styled component for maintainability and reuse

> Move styles to a CSS class, CSS module, Tailwind utilities, or a styled component — inline objects with many properties hurt readability and create new references every render

[`apps/web/src/lib/dotmatrix-core.tsx:784`](https://github.com/celeroncoder/coco/blob/cb87ff57d35b548b0cdbbfd700eec5d569e3263e/apps/web/src/lib/dotmatrix-core.tsx#L784)

---

Last scored May 14, 2026 at 9:39 AM UTC. Maintained by [React Review](https://github.com/millionco/react-review).

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.