| name | arcanea-react-best-practices |
| description | React 19 and Next.js 16 performance optimization for the Arcanea platform. Use when writing, reviewing, or refactoring React components, data fetching logic, bundle optimization, or any frontend performance work. Triggers on: React components, Next.js pages, hooks, data fetching, bundle size, re-renders, Server Components, Client Components, hydration. Sourced from Vercel Engineering's official React best practices (57 rules, 8 categories) and adapted for the Arcanea stack. |
Arcanea React Best Practices
"Leyla guards the Gate of Flow at 285 Hz. Your components must flow, not block. Every waterfall is a Flow violation."
Adapted from Vercel Engineering's 57-rule guide for the Arcanea stack: Next.js 16 App Router + React 19 + TypeScript strict + Supabase + Vercel AI SDK 6.
Rule Categories by Priority
| Priority | Category | Impact | Prefix |
|---|
| 1 | Eliminating Waterfalls | CRITICAL | async- |
| 2 | Bundle Size | CRITICAL | bundle- |
| 3 | Server-Side | HIGH | server- |
| 4 | Client Data Fetching | MEDIUM-HIGH | client- |
| 5 | Re-render Optimization | MEDIUM | rerender- |
| 6 | Rendering Performance | MEDIUM | rendering- |
| 7 | JavaScript Performance | LOW-MEDIUM | js- |
| 8 | Advanced Patterns | LOW | advanced- |
1. Eliminating Waterfalls (CRITICAL)
async-parallel — Use Promise.all for independent fetches
const user = await getUser(id)
const posts = await getUserPosts(id)
const [user, posts] = await Promise.all([getUser(id), getUserPosts(id)])
async-defer-await — Move await to where it's actually needed
async function handler() {
const data = await fetchData()
if (condition) return data
return null
}
async function handler() {
const dataPromise = fetchData()
if (!condition) return null
return await dataPromise
}
async-suspense-boundaries — Stream content with Suspense
export default function LorePage() {
return (
<Suspense fallback={<GlassCardSkeleton />}>
<LoreContent /> {/* streams in independently */}
</Suspense>
)
}
async-api-routes — Start promises early in route handlers
export async function GET() {
const guardianPromise = supabase.from('guardians').select()
const { data } = await guardianPromise
return Response.json(data)
}
2. Bundle Size (CRITICAL)
bundle-barrel-imports — Import directly, never from barrel files
import { Button, Card, Modal } from '@/components/ui'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
bundle-dynamic-imports — Lazy-load heavy components
import dynamic from 'next/dynamic'
const LoreCanvas = dynamic(() => import('@/components/lore/LoreCanvas'), {
loading: () => <GlassCardSkeleton />,
ssr: false,
})
bundle-defer-third-party — Analytics after hydration
import { GoogleAnalytics } from '@next/third-parties/google'
3. Server-Side Performance (HIGH)
server-cache-react — Deduplicate DB calls per request
import { cache } from 'react'
import { supabase } from '@/lib/supabase'
export const getGuardian = cache(async (gateName: string) => {
const { data } = await supabase
.from('guardians')
.select()
.eq('gate', gateName)
.single()
return data
})
server-auth-actions — Always authenticate Server Actions
'use server'
import { createServerClient } from '@/lib/supabase-server'
export async function savePrompt(data: PromptData) {
const supabase = createServerClient()
const { data: { user } } = await supabase.auth.getUser()
if (!user) throw new Error('Unauthorized')
}
server-parallel-fetching — Restructure RSC to parallelize
async function GuardianPage({ gate }: { gate: string }) {
const guardian = await getGuardian(gate)
const godbeast = await getGodbeast(guardian.id)
}
async function GuardianPage({ gate }: { gate: string }) {
const [guardian, godbeast] = await Promise.all([
getGuardian(gate),
getGodBeastByGate(gate),
])
}
4. Client Data Fetching (MEDIUM-HIGH)
client-swr-dedup — SWR/React Query for client fetches
import useSWR from 'swr'
function useGuardians() {
return useSWR('/api/guardians', fetcher, {
revalidateOnFocus: false,
})
}
5. Re-render Optimization (MEDIUM)
rerender-memo — Memoize expensive Arcanea components
import { memo } from 'react'
const GuardianCard = memo(function GuardianCard({ guardian }: Props) {
return <div className="glass-card">...</div>
})
rerender-derived-state-no-effect — Derive during render
const [filteredGuardians, setFiltered] = useState([])
useEffect(() => {
setFiltered(guardians.filter(g => g.element === selectedElement))
}, [guardians, selectedElement])
const filteredGuardians = guardians.filter(g => g.element === selectedElement)
rerender-lazy-state-init — Lazy init for expensive state
const [state, setState] = useState(computeExpensiveDefault())
const [state, setState] = useState(() => computeExpensiveDefault())
rerender-transitions — Non-urgent updates via startTransition
import { useTransition } from 'react'
function GateFilter() {
const [isPending, startTransition] = useTransition()
return (
<select onChange={e => startTransition(() => setGate(e.target.value))}>
{/* Gate filter won't block urgent UI updates */}
</select>
)
}
6. Rendering Performance (MEDIUM)
rendering-hoist-jsx — Extract static JSX outside components
function GuardianList() {
const header = <h2 className="text-gradient-aurora">The Ten Gates</h2>
return <div>{header}{items}</div>
}
const HEADER = <h2 className="text-gradient-aurora">The Ten Gates</h2>
function GuardianList() {
return <div>{HEADER}{items}</div>
}
rendering-conditional-render — Ternary, not &&
{count && <Badge>{count}</Badge>}
{count > 0 ? <Badge>{count}</Badge> : null}
7. JavaScript Performance (LOW-MEDIUM)
js-index-maps — Map for repeated lookups
function getGuardian(gate: string) {
return guardians.find(g => g.gate === gate)
}
const guardianMap = new Map(guardians.map(g => [g.gate, g]))
const getGuardian = (gate: string) => guardianMap.get(gate)
js-early-exit — Return early from functions
function validateGateUnlock(user: User, gate: Gate): ValidationResult {
if (!user.authenticated) return { valid: false, reason: 'unauthenticated' }
if (user.gatesOpen < gate.requiredRank) return { valid: false, reason: 'rank' }
if (gate.locked) return { valid: false, reason: 'sealed' }
return { valid: true }
}
8. Arcanea-Specific Patterns
AI SDK 6 — Vercel AI SDK streaming
import { streamText } from 'ai'
import { google } from '@ai-sdk/google'
export async function POST(req: Request) {
const { messages } = await req.json()
const result = streamText({
model: google('gemini-2.5-flash'),
messages,
maxOutputTokens: 2048,
})
return result.toUIMessageStreamResponse()
}
Supabase Auth in Server Components
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'
export async function getServerUser() {
const cookieStore = cookies()
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{ cookies: { get: (name) => cookieStore.get(name)?.value } }
)
const { data: { user } } = await supabase.auth.getUser()
return user
}
Glass Components — Design System compliance
function GuardianCard({ guardian }: Props) {
return (
<div className="glass hover-lift glow-card">
<h3 className="text-gradient-aurora">{guardian.name}</h3>
</div>
)
}
Quick Checklist
Before any React/Next.js PR in Arcanea: