create-quadrokit 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +3 -3
- package/template-common/.agents/skills/quadro-paged-collection/SKILL.md +33 -0
- package/template-common/.agents/skills/vercel-composition-patterns/AGENTS.md +946 -0
- package/template-common/.agents/skills/vercel-composition-patterns/README.md +60 -0
- package/template-common/.agents/skills/vercel-composition-patterns/SKILL.md +89 -0
- package/template-common/.agents/skills/vercel-composition-patterns/rules/_sections.md +29 -0
- package/template-common/.agents/skills/vercel-composition-patterns/rules/_template.md +24 -0
- package/template-common/.agents/skills/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
- package/template-common/.agents/skills/vercel-composition-patterns/rules/architecture-compound-components.md +112 -0
- package/template-common/.agents/skills/vercel-composition-patterns/rules/patterns-children-over-render-props.md +87 -0
- package/template-common/.agents/skills/vercel-composition-patterns/rules/patterns-explicit-variants.md +100 -0
- package/template-common/.agents/skills/vercel-composition-patterns/rules/react19-no-forwardref.md +42 -0
- package/template-common/.agents/skills/vercel-composition-patterns/rules/state-context-interface.md +191 -0
- package/template-common/.agents/skills/vercel-composition-patterns/rules/state-decouple-implementation.md +113 -0
- package/template-common/.agents/skills/vercel-composition-patterns/rules/state-lift-state.md +125 -0
- package/template-common/.agents/skills/vercel-react-best-practices/AGENTS.md +3750 -0
- package/template-common/.agents/skills/vercel-react-best-practices/README.md +123 -0
- package/template-common/.agents/skills/vercel-react-best-practices/SKILL.md +148 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/_sections.md +46 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/_template.md +28 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/advanced-effect-event-deps.md +56 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/async-defer-await.md +82 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +60 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/js-flatmap-filter.md +60 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/js-request-idle-callback.md +105 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rendering-resource-hints.md +85 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rendering-script-defer-async.md +68 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rerender-no-inline-components.md +82 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rerender-use-deferred-value.md +59 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/server-hoist-static-io.md +149 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/server-no-shared-module-state.md +50 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/server-parallel-nested-fetching.md +34 -0
- package/template-common/.agents/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/template-common/.agents/skills/web-design-guidelines/SKILL.md +39 -0
- package/template-common/skills-lock.json +20 -0
package/template-common/.agents/skills/vercel-react-best-practices/rules/js-request-idle-callback.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Defer Non-Critical Work with requestIdleCallback
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: keeps UI responsive during background tasks
|
|
5
|
+
tags: javascript, performance, idle, scheduling, analytics
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Defer Non-Critical Work with requestIdleCallback
|
|
9
|
+
|
|
10
|
+
**Impact: MEDIUM (keeps UI responsive during background tasks)**
|
|
11
|
+
|
|
12
|
+
Use `requestIdleCallback()` to schedule non-critical work during browser idle periods. This keeps the main thread free for user interactions and animations, reducing jank and improving perceived performance.
|
|
13
|
+
|
|
14
|
+
**Incorrect (blocks main thread during user interaction):**
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
function handleSearch(query: string) {
|
|
18
|
+
const results = searchItems(query)
|
|
19
|
+
setResults(results)
|
|
20
|
+
|
|
21
|
+
// These block the main thread immediately
|
|
22
|
+
analytics.track('search', { query })
|
|
23
|
+
saveToRecentSearches(query)
|
|
24
|
+
prefetchTopResults(results.slice(0, 3))
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Correct (defers non-critical work to idle time):**
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
function handleSearch(query: string) {
|
|
32
|
+
const results = searchItems(query)
|
|
33
|
+
setResults(results)
|
|
34
|
+
|
|
35
|
+
// Defer non-critical work to idle periods
|
|
36
|
+
requestIdleCallback(() => {
|
|
37
|
+
analytics.track('search', { query })
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
requestIdleCallback(() => {
|
|
41
|
+
saveToRecentSearches(query)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
requestIdleCallback(() => {
|
|
45
|
+
prefetchTopResults(results.slice(0, 3))
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**With timeout for required work:**
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
// Ensure analytics fires within 2 seconds even if browser stays busy
|
|
54
|
+
requestIdleCallback(
|
|
55
|
+
() => analytics.track('page_view', { path: location.pathname }),
|
|
56
|
+
{ timeout: 2000 }
|
|
57
|
+
)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Chunking large tasks:**
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
function processLargeDataset(items: Item[]) {
|
|
64
|
+
let index = 0
|
|
65
|
+
|
|
66
|
+
function processChunk(deadline: IdleDeadline) {
|
|
67
|
+
// Process items while we have idle time (aim for <50ms chunks)
|
|
68
|
+
while (index < items.length && deadline.timeRemaining() > 0) {
|
|
69
|
+
processItem(items[index])
|
|
70
|
+
index++
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Schedule next chunk if more items remain
|
|
74
|
+
if (index < items.length) {
|
|
75
|
+
requestIdleCallback(processChunk)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
requestIdleCallback(processChunk)
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**With fallback for unsupported browsers:**
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
const scheduleIdleWork = window.requestIdleCallback ?? ((cb: () => void) => setTimeout(cb, 1))
|
|
87
|
+
|
|
88
|
+
scheduleIdleWork(() => {
|
|
89
|
+
// Non-critical work
|
|
90
|
+
})
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**When to use:**
|
|
94
|
+
|
|
95
|
+
- Analytics and telemetry
|
|
96
|
+
- Saving state to localStorage/IndexedDB
|
|
97
|
+
- Prefetching resources for likely next actions
|
|
98
|
+
- Processing non-urgent data transformations
|
|
99
|
+
- Lazy initialization of non-critical features
|
|
100
|
+
|
|
101
|
+
**When NOT to use:**
|
|
102
|
+
|
|
103
|
+
- User-initiated actions that need immediate feedback
|
|
104
|
+
- Rendering updates the user is waiting for
|
|
105
|
+
- Time-sensitive operations
|
package/template-common/.agents/skills/vercel-react-best-practices/rules/js-set-map-lookups.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Set/Map for O(1) Lookups
|
|
3
|
+
impact: LOW-MEDIUM
|
|
4
|
+
impactDescription: O(n) to O(1)
|
|
5
|
+
tags: javascript, set, map, data-structures, performance
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Set/Map for O(1) Lookups
|
|
9
|
+
|
|
10
|
+
Convert arrays to Set/Map for repeated membership checks.
|
|
11
|
+
|
|
12
|
+
**Incorrect (O(n) per check):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
const allowedIds = ['a', 'b', 'c', ...]
|
|
16
|
+
items.filter(item => allowedIds.includes(item.id))
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Correct (O(1) per check):**
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
const allowedIds = new Set(['a', 'b', 'c', ...])
|
|
23
|
+
items.filter(item => allowedIds.has(item.id))
|
|
24
|
+
```
|
package/template-common/.agents/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use toSorted() Instead of sort() for Immutability
|
|
3
|
+
impact: MEDIUM-HIGH
|
|
4
|
+
impactDescription: prevents mutation bugs in React state
|
|
5
|
+
tags: javascript, arrays, immutability, react, state, mutation
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use toSorted() Instead of sort() for Immutability
|
|
9
|
+
|
|
10
|
+
`.sort()` mutates the array in place, which can cause bugs with React state and props. Use `.toSorted()` to create a new sorted array without mutation.
|
|
11
|
+
|
|
12
|
+
**Incorrect (mutates original array):**
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
function UserList({ users }: { users: User[] }) {
|
|
16
|
+
// Mutates the users prop array!
|
|
17
|
+
const sorted = useMemo(
|
|
18
|
+
() => users.sort((a, b) => a.name.localeCompare(b.name)),
|
|
19
|
+
[users]
|
|
20
|
+
)
|
|
21
|
+
return <div>{sorted.map(renderUser)}</div>
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Correct (creates new array):**
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
function UserList({ users }: { users: User[] }) {
|
|
29
|
+
// Creates new sorted array, original unchanged
|
|
30
|
+
const sorted = useMemo(
|
|
31
|
+
() => users.toSorted((a, b) => a.name.localeCompare(b.name)),
|
|
32
|
+
[users]
|
|
33
|
+
)
|
|
34
|
+
return <div>{sorted.map(renderUser)}</div>
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Why this matters in React:**
|
|
39
|
+
|
|
40
|
+
1. Props/state mutations break React's immutability model - React expects props and state to be treated as read-only
|
|
41
|
+
2. Causes stale closure bugs - Mutating arrays inside closures (callbacks, effects) can lead to unexpected behavior
|
|
42
|
+
|
|
43
|
+
**Browser support (fallback for older browsers):**
|
|
44
|
+
|
|
45
|
+
`.toSorted()` is available in all modern browsers (Chrome 110+, Safari 16+, Firefox 115+, Node.js 20+). For older environments, use spread operator:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// Fallback for older browsers
|
|
49
|
+
const sorted = [...items].sort((a, b) => a.value - b.value)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Other immutable array methods:**
|
|
53
|
+
|
|
54
|
+
- `.toSorted()` - immutable sort
|
|
55
|
+
- `.toReversed()` - immutable reverse
|
|
56
|
+
- `.toSpliced()` - immutable splice
|
|
57
|
+
- `.with()` - immutable element replacement
|
package/template-common/.agents/skills/vercel-react-best-practices/rules/rendering-activity.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Activity Component for Show/Hide
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: preserves state/DOM
|
|
5
|
+
tags: rendering, activity, visibility, state-preservation
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Activity Component for Show/Hide
|
|
9
|
+
|
|
10
|
+
Use React's `<Activity>` to preserve state/DOM for expensive components that frequently toggle visibility.
|
|
11
|
+
|
|
12
|
+
**Usage:**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { Activity } from 'react'
|
|
16
|
+
|
|
17
|
+
function Dropdown({ isOpen }: Props) {
|
|
18
|
+
return (
|
|
19
|
+
<Activity mode={isOpen ? 'visible' : 'hidden'}>
|
|
20
|
+
<ExpensiveMenu />
|
|
21
|
+
</Activity>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Avoids expensive re-renders and state loss.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Animate SVG Wrapper Instead of SVG Element
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: enables hardware acceleration
|
|
5
|
+
tags: rendering, svg, css, animation, performance
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Animate SVG Wrapper Instead of SVG Element
|
|
9
|
+
|
|
10
|
+
Many browsers don't have hardware acceleration for CSS3 animations on SVG elements. Wrap SVG in a `<div>` and animate the wrapper instead.
|
|
11
|
+
|
|
12
|
+
**Incorrect (animating SVG directly - no hardware acceleration):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
function LoadingSpinner() {
|
|
16
|
+
return (
|
|
17
|
+
<svg
|
|
18
|
+
className="animate-spin"
|
|
19
|
+
width="24"
|
|
20
|
+
height="24"
|
|
21
|
+
viewBox="0 0 24 24"
|
|
22
|
+
>
|
|
23
|
+
<circle cx="12" cy="12" r="10" stroke="currentColor" />
|
|
24
|
+
</svg>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Correct (animating wrapper div - hardware accelerated):**
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
function LoadingSpinner() {
|
|
33
|
+
return (
|
|
34
|
+
<div className="animate-spin">
|
|
35
|
+
<svg
|
|
36
|
+
width="24"
|
|
37
|
+
height="24"
|
|
38
|
+
viewBox="0 0 24 24"
|
|
39
|
+
>
|
|
40
|
+
<circle cx="12" cy="12" r="10" stroke="currentColor" />
|
|
41
|
+
</svg>
|
|
42
|
+
</div>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
This applies to all CSS transforms and transitions (`transform`, `opacity`, `translate`, `scale`, `rotate`). The wrapper div allows browsers to use GPU acceleration for smoother animations.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Explicit Conditional Rendering
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: prevents rendering 0 or NaN
|
|
5
|
+
tags: rendering, conditional, jsx, falsy-values
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use Explicit Conditional Rendering
|
|
9
|
+
|
|
10
|
+
Use explicit ternary operators (`? :`) instead of `&&` for conditional rendering when the condition can be `0`, `NaN`, or other falsy values that render.
|
|
11
|
+
|
|
12
|
+
**Incorrect (renders "0" when count is 0):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
function Badge({ count }: { count: number }) {
|
|
16
|
+
return (
|
|
17
|
+
<div>
|
|
18
|
+
{count && <span className="badge">{count}</span>}
|
|
19
|
+
</div>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// When count = 0, renders: <div>0</div>
|
|
24
|
+
// When count = 5, renders: <div><span class="badge">5</span></div>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Correct (renders nothing when count is 0):**
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
function Badge({ count }: { count: number }) {
|
|
31
|
+
return (
|
|
32
|
+
<div>
|
|
33
|
+
{count > 0 ? <span className="badge">{count}</span> : null}
|
|
34
|
+
</div>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// When count = 0, renders: <div></div>
|
|
39
|
+
// When count = 5, renders: <div><span class="badge">5</span></div>
|
|
40
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: CSS content-visibility for Long Lists
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: faster initial render
|
|
5
|
+
tags: rendering, css, content-visibility, long-lists
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## CSS content-visibility for Long Lists
|
|
9
|
+
|
|
10
|
+
Apply `content-visibility: auto` to defer off-screen rendering.
|
|
11
|
+
|
|
12
|
+
**CSS:**
|
|
13
|
+
|
|
14
|
+
```css
|
|
15
|
+
.message-item {
|
|
16
|
+
content-visibility: auto;
|
|
17
|
+
contain-intrinsic-size: 0 80px;
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Example:**
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
function MessageList({ messages }: { messages: Message[] }) {
|
|
25
|
+
return (
|
|
26
|
+
<div className="overflow-y-auto h-screen">
|
|
27
|
+
{messages.map(msg => (
|
|
28
|
+
<div key={msg.id} className="message-item">
|
|
29
|
+
<Avatar user={msg.author} />
|
|
30
|
+
<div>{msg.content}</div>
|
|
31
|
+
</div>
|
|
32
|
+
))}
|
|
33
|
+
</div>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
For 1000 messages, browser skips layout/paint for ~990 off-screen items (10× faster initial render).
|
package/template-common/.agents/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Hoist Static JSX Elements
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: avoids re-creation
|
|
5
|
+
tags: rendering, jsx, static, optimization
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Hoist Static JSX Elements
|
|
9
|
+
|
|
10
|
+
Extract static JSX outside components to avoid re-creation.
|
|
11
|
+
|
|
12
|
+
**Incorrect (recreates element every render):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
function LoadingSkeleton() {
|
|
16
|
+
return <div className="animate-pulse h-20 bg-gray-200" />
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function Container() {
|
|
20
|
+
return (
|
|
21
|
+
<div>
|
|
22
|
+
{loading && <LoadingSkeleton />}
|
|
23
|
+
</div>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Correct (reuses same element):**
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
const loadingSkeleton = (
|
|
32
|
+
<div className="animate-pulse h-20 bg-gray-200" />
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
function Container() {
|
|
36
|
+
return (
|
|
37
|
+
<div>
|
|
38
|
+
{loading && loadingSkeleton}
|
|
39
|
+
</div>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
This is especially helpful for large and static SVG nodes, which can be expensive to recreate on every render.
|
|
45
|
+
|
|
46
|
+
**Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, the compiler automatically hoists static JSX elements and optimizes component re-renders, making manual hoisting unnecessary.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Prevent Hydration Mismatch Without Flickering
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: avoids visual flicker and hydration errors
|
|
5
|
+
tags: rendering, ssr, hydration, localStorage, flicker
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Prevent Hydration Mismatch Without Flickering
|
|
9
|
+
|
|
10
|
+
When rendering content that depends on client-side storage (localStorage, cookies), avoid both SSR breakage and post-hydration flickering by injecting a synchronous script that updates the DOM before React hydrates.
|
|
11
|
+
|
|
12
|
+
**Incorrect (breaks SSR):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
function ThemeWrapper({ children }: { children: ReactNode }) {
|
|
16
|
+
// localStorage is not available on server - throws error
|
|
17
|
+
const theme = localStorage.getItem('theme') || 'light'
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className={theme}>
|
|
21
|
+
{children}
|
|
22
|
+
</div>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Server-side rendering will fail because `localStorage` is undefined.
|
|
28
|
+
|
|
29
|
+
**Incorrect (visual flickering):**
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
function ThemeWrapper({ children }: { children: ReactNode }) {
|
|
33
|
+
const [theme, setTheme] = useState('light')
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
// Runs after hydration - causes visible flash
|
|
37
|
+
const stored = localStorage.getItem('theme')
|
|
38
|
+
if (stored) {
|
|
39
|
+
setTheme(stored)
|
|
40
|
+
}
|
|
41
|
+
}, [])
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div className={theme}>
|
|
45
|
+
{children}
|
|
46
|
+
</div>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Component first renders with default value (`light`), then updates after hydration, causing a visible flash of incorrect content.
|
|
52
|
+
|
|
53
|
+
**Correct (no flicker, no hydration mismatch):**
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
function ThemeWrapper({ children }: { children: ReactNode }) {
|
|
57
|
+
return (
|
|
58
|
+
<>
|
|
59
|
+
<div id="theme-wrapper">
|
|
60
|
+
{children}
|
|
61
|
+
</div>
|
|
62
|
+
<script
|
|
63
|
+
dangerouslySetInnerHTML={{
|
|
64
|
+
__html: `
|
|
65
|
+
(function() {
|
|
66
|
+
try {
|
|
67
|
+
var theme = localStorage.getItem('theme') || 'light';
|
|
68
|
+
var el = document.getElementById('theme-wrapper');
|
|
69
|
+
if (el) el.className = theme;
|
|
70
|
+
} catch (e) {}
|
|
71
|
+
})();
|
|
72
|
+
`,
|
|
73
|
+
}}
|
|
74
|
+
/>
|
|
75
|
+
</>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The inline script executes synchronously before showing the element, ensuring the DOM already has the correct value. No flickering, no hydration mismatch.
|
|
81
|
+
|
|
82
|
+
This pattern is especially useful for theme toggles, user preferences, authentication states, and any client-only data that should render immediately without flashing default values.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Suppress Expected Hydration Mismatches
|
|
3
|
+
impact: LOW-MEDIUM
|
|
4
|
+
impactDescription: avoids noisy hydration warnings for known differences
|
|
5
|
+
tags: rendering, hydration, ssr, nextjs
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Suppress Expected Hydration Mismatches
|
|
9
|
+
|
|
10
|
+
In SSR frameworks (e.g., Next.js), some values are intentionally different on server vs client (random IDs, dates, locale/timezone formatting). For these *expected* mismatches, wrap the dynamic text in an element with `suppressHydrationWarning` to prevent noisy warnings. Do not use this to hide real bugs. Don’t overuse it.
|
|
11
|
+
|
|
12
|
+
**Incorrect (known mismatch warnings):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
function Timestamp() {
|
|
16
|
+
return <span>{new Date().toLocaleString()}</span>
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Correct (suppress expected mismatch only):**
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
function Timestamp() {
|
|
24
|
+
return (
|
|
25
|
+
<span suppressHydrationWarning>
|
|
26
|
+
{new Date().toLocaleString()}
|
|
27
|
+
</span>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
```
|
package/template-common/.agents/skills/vercel-react-best-practices/rules/rendering-resource-hints.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use React DOM Resource Hints
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: reduces load time for critical resources
|
|
5
|
+
tags: rendering, preload, preconnect, prefetch, resource-hints
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use React DOM Resource Hints
|
|
9
|
+
|
|
10
|
+
**Impact: HIGH (reduces load time for critical resources)**
|
|
11
|
+
|
|
12
|
+
React DOM provides APIs to hint the browser about resources it will need. These are especially useful in server components to start loading resources before the client even receives the HTML.
|
|
13
|
+
|
|
14
|
+
- **`prefetchDNS(href)`**: Resolve DNS for a domain you expect to connect to
|
|
15
|
+
- **`preconnect(href)`**: Establish connection (DNS + TCP + TLS) to a server
|
|
16
|
+
- **`preload(href, options)`**: Fetch a resource (stylesheet, font, script, image) you'll use soon
|
|
17
|
+
- **`preloadModule(href)`**: Fetch an ES module you'll use soon
|
|
18
|
+
- **`preinit(href, options)`**: Fetch and evaluate a stylesheet or script
|
|
19
|
+
- **`preinitModule(href)`**: Fetch and evaluate an ES module
|
|
20
|
+
|
|
21
|
+
**Example (preconnect to third-party APIs):**
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { preconnect, prefetchDNS } from 'react-dom'
|
|
25
|
+
|
|
26
|
+
export default function App() {
|
|
27
|
+
prefetchDNS('https://analytics.example.com')
|
|
28
|
+
preconnect('https://api.example.com')
|
|
29
|
+
|
|
30
|
+
return <main>{/* content */}</main>
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Example (preload critical fonts and styles):**
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { preload, preinit } from 'react-dom'
|
|
38
|
+
|
|
39
|
+
export default function RootLayout({ children }) {
|
|
40
|
+
// Preload font file
|
|
41
|
+
preload('/fonts/inter.woff2', { as: 'font', type: 'font/woff2', crossOrigin: 'anonymous' })
|
|
42
|
+
|
|
43
|
+
// Fetch and apply critical stylesheet immediately
|
|
44
|
+
preinit('/styles/critical.css', { as: 'style' })
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<html>
|
|
48
|
+
<body>{children}</body>
|
|
49
|
+
</html>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Example (preload modules for code-split routes):**
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import { preloadModule, preinitModule } from 'react-dom'
|
|
58
|
+
|
|
59
|
+
function Navigation() {
|
|
60
|
+
const preloadDashboard = () => {
|
|
61
|
+
preloadModule('/dashboard.js', { as: 'script' })
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<nav>
|
|
66
|
+
<a href="/dashboard" onMouseEnter={preloadDashboard}>
|
|
67
|
+
Dashboard
|
|
68
|
+
</a>
|
|
69
|
+
</nav>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**When to use each:**
|
|
75
|
+
|
|
76
|
+
| API | Use case |
|
|
77
|
+
|-----|----------|
|
|
78
|
+
| `prefetchDNS` | Third-party domains you'll connect to later |
|
|
79
|
+
| `preconnect` | APIs or CDNs you'll fetch from immediately |
|
|
80
|
+
| `preload` | Critical resources needed for current page |
|
|
81
|
+
| `preloadModule` | JS modules for likely next navigation |
|
|
82
|
+
| `preinit` | Stylesheets/scripts that must execute early |
|
|
83
|
+
| `preinitModule` | ES modules that must execute early |
|
|
84
|
+
|
|
85
|
+
Reference: [React DOM Resource Preloading APIs](https://react.dev/reference/react-dom#resource-preloading-apis)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use defer or async on Script Tags
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: eliminates render-blocking
|
|
5
|
+
tags: rendering, script, defer, async, performance
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Use defer or async on Script Tags
|
|
9
|
+
|
|
10
|
+
**Impact: HIGH (eliminates render-blocking)**
|
|
11
|
+
|
|
12
|
+
Script tags without `defer` or `async` block HTML parsing while the script downloads and executes. This delays First Contentful Paint and Time to Interactive.
|
|
13
|
+
|
|
14
|
+
- **`defer`**: Downloads in parallel, executes after HTML parsing completes, maintains execution order
|
|
15
|
+
- **`async`**: Downloads in parallel, executes immediately when ready, no guaranteed order
|
|
16
|
+
|
|
17
|
+
Use `defer` for scripts that depend on DOM or other scripts. Use `async` for independent scripts like analytics.
|
|
18
|
+
|
|
19
|
+
**Incorrect (blocks rendering):**
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
export default function Document() {
|
|
23
|
+
return (
|
|
24
|
+
<html>
|
|
25
|
+
<head>
|
|
26
|
+
<script src="https://example.com/analytics.js" />
|
|
27
|
+
<script src="/scripts/utils.js" />
|
|
28
|
+
</head>
|
|
29
|
+
<body>{/* content */}</body>
|
|
30
|
+
</html>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Correct (non-blocking):**
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
export default function Document() {
|
|
39
|
+
return (
|
|
40
|
+
<html>
|
|
41
|
+
<head>
|
|
42
|
+
{/* Independent script - use async */}
|
|
43
|
+
<script src="https://example.com/analytics.js" async />
|
|
44
|
+
{/* DOM-dependent script - use defer */}
|
|
45
|
+
<script src="/scripts/utils.js" defer />
|
|
46
|
+
</head>
|
|
47
|
+
<body>{/* content */}</body>
|
|
48
|
+
</html>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Note:** In Next.js, prefer the `next/script` component with `strategy` prop instead of raw script tags:
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import Script from 'next/script'
|
|
57
|
+
|
|
58
|
+
export default function Page() {
|
|
59
|
+
return (
|
|
60
|
+
<>
|
|
61
|
+
<Script src="https://example.com/analytics.js" strategy="afterInteractive" />
|
|
62
|
+
<Script src="/scripts/utils.js" strategy="beforeInteractive" />
|
|
63
|
+
</>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Reference: [MDN - Script element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#defer)
|
package/template-common/.agents/skills/vercel-react-best-practices/rules/rendering-svg-precision.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Optimize SVG Precision
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: reduces file size
|
|
5
|
+
tags: rendering, svg, optimization, svgo
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Optimize SVG Precision
|
|
9
|
+
|
|
10
|
+
Reduce SVG coordinate precision to decrease file size. The optimal precision depends on the viewBox size, but in general reducing precision should be considered.
|
|
11
|
+
|
|
12
|
+
**Incorrect (excessive precision):**
|
|
13
|
+
|
|
14
|
+
```svg
|
|
15
|
+
<path d="M 10.293847 20.847362 L 30.938472 40.192837" />
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Correct (1 decimal place):**
|
|
19
|
+
|
|
20
|
+
```svg
|
|
21
|
+
<path d="M 10.3 20.8 L 30.9 40.2" />
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Automate with SVGO:**
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx svgo --precision=1 --multipass icon.svg
|
|
28
|
+
```
|