- React Development Patterns
- Overview
- Expert guide for building modern React 19 applications with new concurrent features, Server Components, Actions, and advanced patterns. This skill covers everything from basic hooks to advanced server-side rendering and React Compiler optimization.
- When to Use
- Building React 19 components with TypeScript/JavaScript
- Managing component state with useState and useReducer
- Handling side effects with useEffect
- Optimizing performance with useMemo and useCallback
- Creating custom hooks for reusable logic
- Implementing component composition patterns
- Working with refs using useRef
- Using React 19's new features (use(), useOptimistic, useFormStatus)
- Implementing Server Components and Actions
- Working with Suspense and concurrent rendering
- Building forms with new form hooks
- Instructions
- Identify Component Type
-
- Determine if Server Component or Client Component is needed
- Start with Hooks
-
- Use appropriate hooks for state management and side effects
- Implement Component Logic
-
- Build component with proper TypeScript typing
- Add Event Handlers
-
- Create stable references with useCallback where needed
- Optimize Performance
-
- Use useMemo for expensive computations
- Handle Errors
-
- Implement error boundaries for graceful error handling
- Test Components
-
- Write unit tests with React Testing Library
- Examples
- Server Component with Client Interaction
- // Server Component (default)
- async
- function
- ProductPage
- (
- {
- id
- }
- :
- {
- id
- :
- string
- }
- )
- {
- const
- product
- =
- await
- db
- .
- product
- .
- findUnique
- (
- {
- where
- :
- {
- id
- }
- }
- )
- ;
- return
- (
- <
- div
- >
- <
- h1
- >
- {
- product
- .
- name
- }
- </
- h1
- >
- <
- AddToCartButton
- productId
- =
- {
- product
- .
- id
- }
- />
- </
- div
- >
- )
- ;
- }
- // Client Component
- 'use client'
- ;
- function
- AddToCartButton
- (
- {
- productId
- }
- :
- {
- productId
- :
- string
- }
- )
- {
- const
- [
- isPending
- ,
- startTransition
- ]
- =
- useTransition
- (
- )
- ;
- const
- handleAdd
- =
- (
- )
- =>
- {
- startTransition
- (
- async
- (
- )
- =>
- {
- await
- addToCart
- (
- productId
- )
- ;
- }
- )
- ;
- }
- ;
- return
- (
- <
- button
- onClick
- =
- {
- handleAdd
- }
- disabled
- =
- {
- isPending
- }
- >
- {
- isPending
- ?
- 'Adding...'
- :
- 'Add to Cart'
- }
- </
- button
- >
- )
- ;
- }
- Constraints and Warnings
- Server vs Client
-
- Server Components cannot use hooks, event handlers, or browser APIs
- use() Hook
-
- Can only be called during render, not in callbacks or effects
- Server Actions
-
- Must include 'use server' directive at the top of the file
- State Mutations
-
- Never mutate state directly; always create new references
- Effect Dependencies
-
- Always include all dependencies in useEffect arrays
- Key Stability
-
- Use stable IDs for list keys, not array indices
- Memory Leaks
-
- Always clean up subscriptions and event listeners in useEffect
- Core Hooks Patterns
- useState - State Management
- Basic state declaration and updates:
- import
- {
- useState
- }
- from
- 'react'
- ;
- function
- Counter
- (
- )
- {
- const
- [
- count
- ,
- setCount
- ]
- =
- useState
- (
- 0
- )
- ;
- return
- (
- <
- button onClick
- =
- {
- (
- )
- =>
- setCount
- (
- count
- +
- 1
- )
- }
- >
- Count
- :
- {
- count
- }
- <
- /
- button
- >
- )
- ;
- }
- State with initializer function (expensive computation):
- const
- [
- state
- ,
- setState
- ]
- =
- useState
- (
- (
- )
- =>
- {
- const
- initialState
- =
- computeExpensiveValue
- (
- )
- ;
- return
- initialState
- ;
- }
- )
- ;
- Multiple state variables:
- function
- UserProfile
- (
- )
- {
- const
- [
- name
- ,
- setName
- ]
- =
- useState
- (
- ''
- )
- ;
- const
- [
- age
- ,
- setAge
- ]
- =
- useState
- (
- 0
- )
- ;
- const
- [
- ,
- setEmail
- ]
- =
- useState
- (
- ''
- )
- ;
- return
- (
- <
- form
- >
- <
- input value
- =
- {
- name
- }
- onChange
- =
- {
- e
- =>
- setName
- (
- e
- .
- target
- .
- value
- )
- }
- /
- >
- <
- input type
- =
- "number"
- value
- =
- {
- age
- }
- onChange
- =
- {
- e
- =>
- setAge
- (
- Number
- (
- e
- .
- target
- .
- value
- )
- )
- }
- /
- >
- <
- input type
- =
- "email"
- value
- =
- {
- }
- onChange
- =
- {
- e
- =>
- setEmail
- (
- e
- .
- target
- .
- value
- )
- }
- /
- >
- <
- /
- form
- >
- )
- ;
- }
- useEffect - Side Effects
- Basic effect with cleanup:
- import
- {
- useEffect
- }
- from
- 'react'
- ;
- function
- ChatRoom
- (
- {
- roomId
- }
- :
- {
- roomId
- :
- string
- }
- )
- {
- useEffect
- (
- (
- )
- =>
- {
- const
- connection
- =
- createConnection
- (
- roomId
- )
- ;
- connection
- .
- connect
- (
- )
- ;
- // Cleanup function
- return
- (
- )
- =>
- {
- connection
- .
- disconnect
- (
- )
- ;
- }
- ;
- }
- ,
- [
- roomId
- ]
- )
- ;
- // Dependency array
- return
- <
- div
- >
- Connected to
- {
- roomId
- }
- <
- /
- div
- >
- ;
- }
- Effect with multiple dependencies:
- function
- ChatRoom
- (
- {
- roomId
- ,
- serverUrl
- }
- :
- {
- roomId
- :
- string
- ;
- serverUrl
- :
- string
- }
- )
- {
- useEffect
- (
- (
- )
- =>
- {
- const
- connection
- =
- createConnection
- (
- serverUrl
- ,
- roomId
- )
- ;
- connection
- .
- connect
- (
- )
- ;
- return
- (
- )
- =>
- connection
- .
- disconnect
- (
- )
- ;
- }
- ,
- [
- roomId
- ,
- serverUrl
- ]
- )
- ;
- // Re-run when either changes
- return
- <
- h1
- >
- Welcome to
- {
- roomId
- }
- <
- /
- h1
- >
- ;
- }
- Effect for subscriptions:
- function
- StatusBar
- (
- )
- {
- const
- [
- isOnline
- ,
- setIsOnline
- ]
- =
- useState
- (
- true
- )
- ;
- useEffect
- (
- (
- )
- =>
- {
- function
- handleOnline
- (
- )
- {
- setIsOnline
- (
- true
- )
- ;
- }
- function
- handleOffline
- (
- )
- {
- setIsOnline
- (
- false
- )
- ;
- }
- window
- .
- addEventListener
- (
- 'online'
- ,
- handleOnline
- )
- ;
- window
- .
- addEventListener
- (
- 'offline'
- ,
- handleOffline
- )
- ;
- return
- (
- )
- =>
- {
- window
- .
- removeEventListener
- (
- 'online'
- ,
- handleOnline
- )
- ;
- window
- .
- removeEventListener
- (
- 'offline'
- ,
- handleOffline
- )
- ;
- }
- ;
- }
- ,
- [
- ]
- )
- ;
- // Empty array = run once on mount
- return
- <
- h1
- >
- {
- isOnline
- ?
- '✅ Online'
- :
- '❌ Disconnected'
- }
- <
- /
- h1
- >
- ;
- }
- useRef - Persistent References
- Storing mutable values without re-renders:
- import
- {
- useRef
- }
- from
- 'react'
- ;
- function
- Timer
- (
- )
- {
- const
- intervalRef
- =
- useRef
- <
- NodeJS
- .
- Timeout
- |
- null
- >
- (
- null
- )
- ;
- const
- startTimer
- =
- (
- )
- =>
- {
- intervalRef
- .
- current
- =
- setInterval
- (
- (
- )
- =>
- {
- console
- .
- log
- (
- 'Tick'
- )
- ;
- }
- ,
- 1000
- )
- ;
- }
- ;
- const
- stopTimer
- =
- (
- )
- =>
- {
- if
- (
- intervalRef
- .
- current
- )
- {
- clearInterval
- (
- intervalRef
- .
- current
- )
- ;
- }
- }
- ;
- return
- (
- <
- >
- <
- button onClick
- =
- {
- startTimer
- }
- >
- Start
- <
- /
- button
- >
- <
- button onClick
- =
- {
- stopTimer
- }
- >
- Stop
- <
- /
- button
- >
- <
- /
- >
- )
- ;
- }
- DOM element references:
- function
- TextInput
- (
- )
- {
- const
- inputRef
- =
- useRef
- <
- HTMLInputElement
- >
- (
- null
- )
- ;
- const
- focusInput
- =
- (
- )
- =>
- {
- inputRef
- .
- current
- ?.
- focus
- (
- )
- ;
- }
- ;
- return
- (
- <
- >
- <
- input ref
- =
- {
- inputRef
- }
- type
- =
- "text"
- /
- >
- <
- button onClick
- =
- {
- focusInput
- }
- >
- Focus Input
- <
- /
- button
- >
- <
- /
- >
- )
- ;
- }
- Custom Hooks Pattern
- Extract reusable logic into custom hooks:
- // useOnlineStatus.ts
- import
- {
- useState
- ,
- useEffect
- }
- from
- 'react'
- ;
- export
- function
- useOnlineStatus
- (
- )
- {
- const
- [
- isOnline
- ,
- setIsOnline
- ]
- =
- useState
- (
- true
- )
- ;
- useEffect
- (
- (
- )
- =>
- {
- function
- handleOnline
- (
- )
- {
- setIsOnline
- (
- true
- )
- ;
- }
- function
- handleOffline
- (
- )
- {
- setIsOnline
- (
- false
- )
- ;
- }
- window
- .
- addEventListener
- (
- 'online'
- ,
- handleOnline
- )
- ;
- window
- .
- addEventListener
- (
- 'offline'
- ,
- handleOffline
- )
- ;
- return
- (
- )
- =>
- {
- window
- .
- removeEventListener
- (
- 'online'
- ,
- handleOnline
- )
- ;
- window
- .
- removeEventListener
- (
- 'offline'
- ,
- handleOffline
- )
- ;
- }
- ;
- }
- ,
- [
- ]
- )
- ;
- return
- isOnline
- ;
- }
- // Usage in components
- function
- StatusBar
- (
- )
- {
- const
- isOnline
- =
- useOnlineStatus
- (
- )
- ;
- return
- <
- h1
- >
- {
- isOnline
- ?
- '✅ Online'
- :
- '❌ Disconnected'
- }
- <
- /
- h1
- >
- ;
- }
- function
- SaveButton
- (
- )
- {
- const
- isOnline
- =
- useOnlineStatus
- (
- )
- ;
- return
- (
- <
- button disabled
- =
- {
- !
- isOnline
- }
- >
- {
- isOnline
- ?
- 'Save'
- :
- 'Reconnecting...'
- }
- <
- /
- button
- >
- )
- ;
- }
- Custom hook with parameters:
- // useChatRoom.ts
- import
- {
- useEffect
- }
- from
- 'react'
- ;
- interface
- ChatOptions
- {
- serverUrl
- :
- string
- ;
- roomId
- :
- string
- ;
- }
- export
- function
- useChatRoom
- (
- {
- serverUrl
- ,
- roomId
- }
- :
- ChatOptions
- )
- {
- useEffect
- (
- (
- )
- =>
- {
- const
- connection
- =
- createConnection
- (
- serverUrl
- ,
- roomId
- )
- ;
- connection
- .
- connect
- (
- )
- ;
- return
- (
- )
- =>
- connection
- .
- disconnect
- (
- )
- ;
- }
- ,
- [
- serverUrl
- ,
- roomId
- ]
- )
- ;
- }
- // Usage
- function
- ChatRoom
- (
- {
- roomId
- }
- :
- {
- roomId
- :
- string
- }
- )
- {
- const
- [
- serverUrl
- ,
- setServerUrl
- ]
- =
- useState
- (
- 'https://localhost:1234'
- )
- ;
- useChatRoom
- (
- {
- serverUrl
- ,
- roomId
- }
- )
- ;
- return
- (
- <
- >
- <
- input value
- =
- {
- serverUrl
- }
- onChange
- =
- {
- e
- =>
- setServerUrl
- (
- e
- .
- target
- .
- value
- )
- }
- /
- >
- <
- h1
- >
- Welcome to
- {
- roomId
- }
- <
- /
- h1
- >
- <
- /
- >
- )
- ;
- }
- Component Composition Patterns
- Props and Children
- Basic component with props:
- interface
- ButtonProps
- {
- variant
- ?
- :
- 'primary'
- |
- 'secondary'
- ;
- size
- ?
- :
- 'sm'
- |
- 'md'
- |
- 'lg'
- ;
- onClick
- ?
- :
- (
- )
- =>
- void
- ;
- children
- :
- React
- .
- ReactNode
- ;
- }
- function
- Button
- (
- {
- variant
- =
- 'primary'
- ,
- size
- =
- 'md'
- ,
- onClick
- ,
- children
- }
- :
- ButtonProps
- )
- {
- return
- (
- <
- button
- className
- =
- {
- `
- btn btn-
- ${
- variant
- }
- btn-
- ${
- size
- }
- `
- }
- onClick
- =
- {
- onClick
- }
- >
- {
- children
- }
- <
- /
- button
- >
- )
- ;
- }
- Composition with children:
- interface
- CardProps
- {
- children
- :
- React
- .
- ReactNode
- ;
- className
- ?
- :
- string
- ;
- }
- function
- Card
- (
- {
- children
- ,
- className
- =
- ''
- }
- :
- CardProps
- )
- {
- return
- (
- <
- div className
- =
- {
- `
- card
- ${
- className
- }
- `
- }
- >
- {
- children
- }
- <
- /
- div
- >
- )
- ;
- }
- // Usage
- function
- UserProfile
- (
- )
- {
- return
- (
- <
- Card
- >
- <
- h2
- >
- John Doe
- <
- /
- h2
- >
- <
- p
- >
- Software Engineer
- <
- /
- p
- >
- <
- /
- Card
- >
- )
- ;
- }
- Lifting State Up
- Shared state between siblings:
- function
- Parent
- (
- )
- {
- const
- [
- activeIndex
- ,
- setActiveIndex
- ]
- =
- useState
- (
- 0
- )
- ;
- return
- (
- <
- >
- <
- Panel
- isActive
- =
- {
- activeIndex
- ===
- 0
- }
- onShow
- =
- {
- (
- )
- =>
- setActiveIndex
- (
- 0
- )
- }
- >
- Panel
- 1
- content
- <
- /
- Panel
- >
- <
- Panel
- isActive
- =
- {
- activeIndex
- ===
- 1
- }
- onShow
- =
- {
- (
- )
- =>
- setActiveIndex
- (
- 1
- )
- }
- >
- Panel
- 2
- content
- <
- /
- Panel
- >
- <
- /
- >
- )
- ;
- }
- interface
- PanelProps
- {
- isActive
- :
- boolean
- ;
- onShow
- :
- (
- )
- =>
- void
- ;
- children
- :
- React
- .
- ReactNode
- ;
- }
- function
- Panel
- (
- {
- isActive
- ,
- onShow
- ,
- children
- }
- :
- PanelProps
- )
- {
- return
- (
- <
- div
- >
- <
- button onClick
- =
- {
- onShow
- }
- >
- Show
- <
- /
- button
- >
- {
- isActive
- &&
- <
- div
- >
- {
- children
- }
- <
- /
- div
- >
- }
- <
- /
- div
- >
- )
- ;
- }
- Performance Optimization
- Avoid Unnecessary Effects
- ❌ Bad - Using effect for derived state:
- function
- TodoList
- (
- {
- todos
- }
- :
- {
- todos
- :
- Todo
- [
- ]
- }
- )
- {
- const
- [
- visibleTodos
- ,
- setVisibleTodos
- ]
- =
- useState
- <
- Todo
- [
- ]
- >
- (
- [
- ]
- )
- ;
- useEffect
- (
- (
- )
- =>
- {
- setVisibleTodos
- (
- todos
- .
- filter
- (
- t
- =>
- !
- t
- .
- completed
- )
- )
- ;
- }
- ,
- [
- todos
- ]
- )
- ;
- // Unnecessary effect
- return
- <
- ul
- >
- {
- / ... /
- }
- <
- /
- ul
- >
- ;
- }
- ✅ Good - Compute during render:
- function
- TodoList
- (
- {
- todos
- }
- :
- {
- todos
- :
- Todo
- [
- ]
- }
- )
- {
- const
- visibleTodos
- =
- todos
- .
- filter
- (
- t
- =>
- !
- t
- .
- completed
- )
- ;
- // Direct computation
- return
- <
- ul
- >
- {
- / ... /
- }
- <
- /
- ul
- >
- ;
- }
- useMemo for Expensive Computations
- import
- {
- useMemo
- }
- from
- 'react'
- ;
- function
- DataTable
- (
- {
- data
- }
- :
- {
- data
- :
- Item
- [
- ]
- }
- )
- {
- const
- sortedData
- =
- useMemo
- (
- (
- )
- =>
- {
- return
- [
- ...
- data
- ]
- .
- sort
- (
- (
- a
- ,
- b
- )
- =>
- a
- .
- name
- .
- localeCompare
- (
- b
- .
- name
- )
- )
- ;
- }
- ,
- [
- data
- ]
- )
- ;
- // Only recompute when data changes
- return
- <
- table
- >
- {
- / render sortedData /
- }
- <
- /
- table
- >
- ;
- }
- useCallback for Function Stability
- import
- {
- useCallback
- }
- from
- 'react'
- ;
- function
- Parent
- (
- )
- {
- const
- [
- count
- ,
- setCount
- ]
- =
- useState
- (
- 0
- )
- ;
- const
- handleClick
- =
- useCallback
- (
- (
- )
- =>
- {
- console
- .
- log
- (
- 'Clicked'
- ,
- count
- )
- ;
- }
- ,
- [
- count
- ]
- )
- ;
- // Recreate only when count changes
- return
- <
- ExpensiveChild onClick
- =
- {
- handleClick
- }
- /
- >
- ;
- }
- TypeScript Best Practices
- Type-Safe Props
- interface
- UserProps
- {
- id
- :
- string
- ;
- name
- :
- string
- ;
- :
- string
- ;
- age
- ?
- :
- number
- ;
- // Optional
- }
- function
- User
- (
- {
- id
- ,
- name
- ,
- ,
- age
- }
- :
- UserProps
- )
- {
- return
- (
- <
- div
- >
- <
- h2
- >
- {
- name
- }
- <
- /
- h2
- >
- <
- p
- >
- {
- }
- <
- /
- p
- >
- {
- age
- &&
- <
- p
- >
- Age
- :
- {
- age
- }
- <
- /
- p
- >
- }
- <
- /
- div
- >
- )
- ;
- }
- Generic Components
- interface
- ListProps
- <
- T
- >
- {
- items
- :
- T
- [
- ]
- ;
- renderItem
- :
- (
- item
- :
- T
- )
- =>
- React
- .
- ReactNode
- ;
- }
- function
- List
- <
- T
- >
- (
- {
- items
- ,
- renderItem
- }
- :
- ListProps
- <
- T
- >
- )
- {
- return
- (
- <
- ul
- >
- {
- items
- .
- map
- (
- (
- item
- ,
- index
- )
- =>
- (
- <
- li key
- =
- {
- index
- }
- >
- {
- renderItem
- (
- item
- )
- }
- <
- /
- li
- >
- )
- )
- }
- <
- /
- ul
- >
- )
- ;
- }
- // Usage
- <
- List
- items
- =
- {
- users
- }
- renderItem
- =
- {
- (
- user
- )
- =>
- <
- span
- >
- {
- user
- .
- name
- }
- <
- /
- span
- >
- }
- /
- >
- Event Handlers
- function
- Form
- (
- )
- {
- const
- handleSubmit
- =
- (
- e
- :
- React
- .
- FormEvent
- <
- HTMLFormElement
- >
- )
- =>
- {
- e
- .
- preventDefault
- (
- )
- ;
- // Handle form submission
- }
- ;
- const
- handleChange
- =
- (
- e
- :
- React
- .
- ChangeEvent
- <
- HTMLInputElement
- >
- )
- =>
- {
- console
- .
- log
- (
- e
- .
- target
- .
- value
- )
- ;
- }
- ;
- return
- (
- <
- form onSubmit
- =
- {
- handleSubmit
- }
- >
- <
- input onChange
- =
- {
- handleChange
- }
- /
- >
- <
- /
- form
- >
- )
- ;
- }
- Common Patterns
- Controlled Components
- function
- ControlledInput
- (
- )
- {
- const
- [
- value
- ,
- setValue
- ]
- =
- useState
- (
- ''
- )
- ;
- return
- (
- <
- input
- value
- =
- {
- value
- }
- onChange
- =
- {
- e
- =>
- setValue
- (
- e
- .
- target
- .
- value
- )
- }
- /
- >
- )
- ;
- }
- Conditional Rendering
- function
- Greeting
- (
- {
- isLoggedIn
- }
- :
- {
- isLoggedIn
- :
- boolean
- }
- )
- {
- return
- (
- <
- div
- >
- {
- isLoggedIn
- ?
- (
- <
- UserGreeting
- /
- >
- )
- :
- (
- <
- GuestGreeting
- /
- >
- )
- }
- <
- /
- div
- >
- )
- ;
- }
- Lists and Keys
- function
- UserList
- (
- {
- users
- }
- :
- {
- users
- :
- User
- [
- ]
- }
- )
- {
- return
- (
- <
- ul
- >
- {
- users
- .
- map
- (
- user
- =>
- (
- <
- li key
- =
- {
- user
- .
- id
- }
- >
- {
- user
- .
- name
- }
- <
- /
- li
- >
- )
- )
- }
- <
- /
- ul
- >
- )
- ;
- }
- Best Practices
- General React Best Practices
- Dependency Arrays
-
- Always specify correct dependencies in useEffect
- State Structure
-
- Keep state minimal and avoid redundant state
- Component Size
-
- Keep components small and focused
- Custom Hooks
-
- Extract complex logic into reusable custom hooks
- TypeScript
-
- Use TypeScript for type safety
- Keys
-
- Use stable IDs as keys for list items, not array indices
- Immutability
-
- Never mutate state directly
- Effects
-
- Use effects only for synchronization with external systems
- Performance
-
- Profile before optimizing with useMemo/useCallback
- React 19 Specific Best Practices
- Server Components
-
- Use Server Components for data fetching and static content
- Client Components
-
- Mark components as 'use client' only when necessary
- Actions
-
- Use Server Actions for mutations and form submissions
- Optimistic Updates
-
- Implement useOptimistic for better UX
- use() Hook
-
- Use for reading promises and context conditionally
- Form State
-
- Use useFormState and useFormStatus for complex forms
- Concurrent Features
-
- Leverage useTransition for non-urgent updates
- Error Boundaries
- Implement proper error handling with error boundaries
Common Pitfalls
General React Pitfalls
❌
Missing Dependencies
:
useEffect
(
(
)
=>
{
// Uses 'count' but doesn't include it in deps
console
.
log
(
count
)
;
}
,
[
]
)
;
// Wrong!
❌
Mutating State
:
const
[
items
,
setItems
]
=
useState
(
[
]
)
;
items
.
push
(
newItem
)
;
// Wrong! Mutates state
setItems
(
items
)
;
// Won't trigger re-render
✅
Correct Approach
:
setItems
(
[
...
items
,
newItem
]
)
;
// Create new array
React 19 Specific Pitfalls
❌
Using use() outside of render
:
// Wrong!
function
handleClick
(
)
{
const
data
=
use
(
promise
)
;
// Error: use() can only be called in render
}
✅
Correct usage
:
function
Component
(
{
promise
}
)
{
const
data
=
use
(
promise
)
;
// Correct: called during render
return
<
div
{ data } </ div
; } ❌ Forgetting 'use server' directive : // Wrong - missing 'use server' export async function myAction ( ) { // This will run on the client! } ✅ Correct Server Action : 'use server' ; // Must be at the top export async function myAction ( ) { // Now runs on the server } ❌ Mixing Server and Client logic incorrectly : // Wrong - trying to use browser APIs in Server Component export default async function ServerComponent ( ) { const width = window . innerWidth ; // Error: window is not defined return < div
{ width } </ div
; } ✅ Correct separation : // Server Component for data export default async function ServerComponent ( ) { const data = await fetchData ( ) ; return < ClientComponent data = { data } /> ; } // Client Component for browser APIs 'use client' ; function ClientComponent ( { data } ) { const [ width , setWidth ] = useState ( window . innerWidth ) ; // Handle resize logic... return < div
{ width } </ div
; } React 19 New Features use() Hook - Reading Resources The use() hook reads the value from a resource like a Promise or Context: import { use } from 'react' ; // Reading a Promise in a component function MessageComponent ( { messagePromise } ) { const message = use ( messagePromise ) ; return < p
{ message } < / p
; } // Reading Context conditionally function Button ( ) { if ( condition ) { const theme = use ( ThemeContext ) ; return < button className = { theme }
Click < / button
; } return < button
Click < / button
; } useOptimistic Hook - Optimistic UI Updates Manage optimistic UI updates for async operations: import { useOptimistic } from 'react' ; function TodoList ( { todos , addTodo } ) { const [ optimisticTodos , addOptimisticTodo ] = useOptimistic ( todos , ( state , newTodo ) => [ ... state , newTodo ] ) ; const handleSubmit = async ( formData ) => { const newTodo = { id : Date . now ( ) , text : formData . get ( 'text' ) } ; // Optimistically add to UI addOptimisticTodo ( newTodo ) ; // Actually add to backend await addTodo ( newTodo ) ; } ; return ( < form action = { handleSubmit }
{ optimisticTodos . map ( todo => ( < div key = { todo . id }
{ todo . text } < / div
) ) } < input type = "text" name = "text" /
< button type = "submit"
Add Todo < / button
< / form
) ; } useFormStatus Hook - Form State Access form submission status from child components: import { useFormStatus } from 'react' ; function SubmitButton ( ) { const { pending , data } = useFormStatus ( ) ; return ( < button type = "submit" disabled = { pending }
{ pending ? 'Submitting...' : 'Submit' } < / button
) ; } function ContactForm ( ) { return ( < form action = { submitForm }
< input name = "email" type = "email" /
< SubmitButton /
< / form
) ; } useFormState Hook - Form State Management Manage form state with error handling: import { useFormState } from 'react' ; async function submitAction ( prevState : string | null , formData : FormData ) { const email = formData . get ( 'email' ) as string ; if ( ! email . includes ( '@' ) ) { return 'Invalid email address' ; } await submitToDatabase ( email ) ; return null ; } function EmailForm ( ) { const [ state , formAction ] = useFormState ( submitAction , null ) ; return ( < form action = { formAction }
< input name = "email" type = "email" /
< button type = "submit"
Subscribe < / button
{ state && < p className = "error"
{ state } < / p
} < / form
) ; } Server Actions Define server-side functions for form handling: // app/actions.ts 'use server' ; import { redirect } from 'next/navigation' ; import { revalidatePath } from 'next/cache' ; export async function createPost ( formData : FormData ) { const title = formData . get ( 'title' ) as string ; const content = formData . get ( 'content' ) as string ; // Validate input if ( ! title || ! content ) { return { error : 'Title and content are required' } ; } // Save to database const post = await db . post . create ( { data : { title , content } } ) ; // Update cache and redirect revalidatePath ( '/posts' ) ; redirect (
/posts/ ${ post . id }) ; } Server Components Components that run exclusively on the server: // app/posts/page.tsx - Server Component async function PostsPage ( ) { // Server-side data fetching const posts = await db . post . findMany ( { orderBy : { createdAt : 'desc' } , take : 10 } ) ; return ( < div< h1
Latest Posts < / h1
< PostsList posts = { posts } /
< / div
) ; } // Client Component for interactivity 'use client' ; function PostsList ( { posts } : { posts : Post [ ] } ) { const [ selectedId , setSelectedId ] = useState < number | null
( null ) ; return ( < ul
{ posts . map ( post => ( < li key = { post . id } onClick = { ( ) => setSelectedId ( post . id ) } className = { selectedId === post . id ? 'selected' : '' }
{ post . title } < / li
) ) } < / ul
) ; } React Compiler Automatic Optimization React Compiler automatically optimizes your components: // Before React Compiler - manual memoization needed const ExpensiveComponent = memo ( function ExpensiveComponent ( { data , onUpdate } ) { const processedData = useMemo ( ( ) => { return data . map ( item => ( { ... item , computed : expensiveCalculation ( item ) } ) ) ; } , [ data ] ) ; const handleClick = useCallback ( ( id ) => { onUpdate ( id ) ; } , [ onUpdate ] ) ; return ( < div
{ processedData . map ( item => ( < Item key = { item . id } item = { item } onClick = { handleClick } /
) ) } < / div
) ; } ) ; // After React Compiler - no manual optimization needed function ExpensiveComponent ( { data , onUpdate } ) { const processedData = data . map ( item => ( { ... item , computed : expensiveCalculation ( item ) } ) ) ; const handleClick = ( id ) => { onUpdate ( id ) ; } ; return ( < div
{ processedData . map ( item => ( < Item key = { item . id } item = { item } onClick = { handleClick } /
) ) } < / div
) ; } Installation and Setup
Install React Compiler
npm install -D babel-plugin-react-compiler@latest
Install ESLint plugin for validation
- npm
- install
- -D
- eslint-plugin-react-hooks@latest
- // babel.config.js
- module
- .
- exports
- =
- {
- plugins
- :
- [
- 'babel-plugin-react-compiler'
- ,
- // Must run first!
- // ... other plugins
- ]
- ,
- }
- ;
- // vite.config.js for Vite users
- import
- {
- defineConfig
- }
- from
- 'vite'
- ;
- import
- react
- from
- '@vitejs/plugin-react'
- ;
- export
- default
- defineConfig
- (
- {
- plugins
- :
- [
- react
- (
- {
- babel
- :
- {
- plugins
- :
- [
- 'babel-plugin-react-compiler'
- ]
- ,
- }
- ,
- }
- )
- ,
- ]
- ,
- }
- )
- ;
- Compiler Configuration
- // babel.config.js with compiler options
- module
- .
- exports
- =
- {
- plugins
- :
- [
- [
- 'babel-plugin-react-compiler'
- ,
- {
- // Enable compilation for specific files
- target
- :
- '18'
- ,
- // or '19'
- // Debug mode for development
- debug
- :
- process
- .
- env
- .
- NODE_ENV
- ===
- 'development'
- }
- ]
- ,
- ]
- ,
- }
- ;
- // Incremental adoption with overrides
- module
- .
- exports
- =
- {
- plugins
- :
- [
- ]
- ,
- overrides
- :
- [
- {
- test
- :
- './src/components/*/.{js,jsx,ts,tsx}'
- ,
- plugins
- :
- [
- 'babel-plugin-react-compiler'
- ]
- }
- ]
- }
- ;
- Advanced Server Components Patterns
- Mixed Server/Client Architecture
- // Server Component for data fetching
- async
- function
- ProductPage
- (
- {
- id
- }
- :
- {
- id
- :
- string
- }
- )
- {
- const
- product
- =
- await
- fetchProduct
- (
- id
- )
- ;
- const
- related
- =
- await
- fetchRelatedProducts
- (
- id
- )
- ;
- return
- (
- <
- div
- >
- <
- ProductDetails product
- =
- {
- product
- }
- /
- >
- <
- ProductGallery images
- =
- {
- product
- .
- images
- }
- /
- >
- <
- RelatedProducts products
- =
- {
- related
- }
- /
- >
- <
- /
- div
- >
- )
- ;
- }
- // Client Component for interactivity
- 'use client'
- ;
- function
- ProductDetails
- (
- {
- product
- }
- :
- {
- product
- :
- Product
- }
- )
- {
- const
- [
- quantity
- ,
- setQuantity
- ]
- =
- useState
- (
- 1
- )
- ;
- const
- [
- isAdded
- ,
- setIsAdded
- ]
- =
- useState
- (
- false
- )
- ;
- return
- (
- <
- div
- >
- <
- h1
- >
- {
- product
- .
- name
- }
- <
- /
- h1
- >
- <
- p
- >
- {
- product
- .
- description
- }
- <
- /
- p
- >
- <
- p
- >
- $
- {
- product
- .
- price
- }
- <
- /
- p
- >
- <
- QuantitySelector
- value
- =
- {
- quantity
- }
- onChange
- =
- {
- setQuantity
- }
- /
- >
- <
- AddToCartButton
- productId
- =
- {
- product
- .
- id
- }
- quantity
- =
- {
- quantity
- }
- onAdded
- =
- {
- (
- )
- =>
- setIsAdded
- (
- true
- )
- }
- /
- >
- {
- isAdded
- &&
- <
- p
- >
- Added to cart
- !
- <
- /
- p
- >
- }
- <
- /
- div
- >
- )
- ;
- }
- Server Actions with Validation
- 'use server'
- ;
- import
- {
- z
- }
- from
- 'zod'
- ;
- const
- checkoutSchema
- =
- z
- .
- object
- (
- {
- items
- :
- z
- .
- array
- (
- z
- .
- object
- (
- {
- productId
- :
- z
- .
- string
- (
- )
- ,
- quantity
- :
- z
- .
- number
- (
- )
- .
- min
- (
- 1
- )
- }
- )
- )
- ,
- shippingAddress
- :
- z
- .
- object
- (
- {
- street
- :
- z
- .
- string
- (
- )
- .
- min
- (
- 1
- )
- ,
- city
- :
- z
- .
- string
- (
- )
- .
- min
- (
- 1
- )
- ,
- zipCode
- :
- z
- .
- string
- (
- )
- .
- regex
- (
- /
- ^
- \d
- {5}
- $
- /
- )
- }
- )
- ,
- paymentMethod
- :
- z
- .
- enum
- (
- [
- 'credit'
- ,
- 'paypal'
- ,
- 'apple'
- ]
- )
- }
- )
- ;
- export
- async
- function
- processCheckout
- (
- prevState
- :
- any
- ,
- formData
- :
- FormData
- )
- {
- // Extract and validate data
- const
- rawData
- =
- {
- items
- :
- JSON
- .
- parse
- (
- formData
- .
- get
- (
- 'items'
- )
- as
- string
- )
- ,
- shippingAddress
- :
- {
- street
- :
- formData
- .
- get
- (
- 'street'
- )
- ,
- city
- :
- formData
- .
- get
- (
- 'city'
- )
- ,
- zipCode
- :
- formData
- .
- get
- (
- 'zipCode'
- )
- }
- ,
- paymentMethod
- :
- formData
- .
- get
- (
- 'paymentMethod'
- )
- }
- ;
- const
- result
- =
- checkoutSchema
- .
- safeParse
- (
- rawData
- )
- ;
- if
- (
- !
- result
- .
- success
- )
- {
- return
- {
- error
- :
- 'Validation failed'
- ,
- fieldErrors
- :
- result
- .
- error
- .
- flatten
- (
- )
- .
- fieldErrors
- }
- ;
- }
- try
- {
- // Process payment
- const
- order
- =
- await
- createOrder
- (
- result
- .
- data
- )
- ;
- // Update inventory
- await
- updateInventory
- (
- result
- .
- data
- .
- items
- )
- ;
- // Send confirmation
- await
- sendConfirmationEmail
- (
- order
- )
- ;
- // Revalidate cache
- revalidatePath
- (
- '/orders'
- )
- ;
- return
- {
- success
- :
- true
- ,
- orderId
- :
- order
- .
- id
- }
- ;
- }
- catch
- (
- error
- )
- {
- return
- {
- error
- :
- 'Payment failed'
- }
- ;
- }
- }
- Concurrent Features
- useTransition for Non-Urgent Updates
- import
- {
- useTransition
- ,
- useState
- }
- from
- 'react'
- ;
- function
- SearchableList
- (
- {
- items
- }
- :
- {
- items
- :
- Item
- [
- ]
- }
- )
- {
- const
- [
- query
- ,
- setQuery
- ]
- =
- useState
- (
- ''
- )
- ;
- const
- [
- isPending
- ,
- startTransition
- ]
- =
- useTransition
- (
- )
- ;
- const
- [
- filteredItems
- ,
- setFilteredItems
- ]
- =
- useState
- (
- items
- )
- ;
- const
- handleChange
- =
- (
- e
- :
- React
- .
- ChangeEvent
- <
- HTMLInputElement
- >
- )
- =>
- {
- // Update input immediately
- setQuery
- (
- e
- .
- target
- .
- value
- )
- ;
- // Transition the filter operation
- startTransition
- (
- (
- )
- =>
- {
- setFilteredItems
- (
- items
- .
- filter
- (
- item
- =>
- item
- .
- name
- .
- toLowerCase
- (
- )
- .
- includes
- (
- e
- .
- target
- .
- value
- .
- toLowerCase
- (
- )
- )
- )
- )
- ;
- }
- )
- ;
- }
- ;
- return
- (
- <
- div
- >
- <
- input
- type
- =
- "text"
- value
- =
- {
- query
- }
- onChange
- =
- {
- handleChange
- }
- placeholder
- =
- "Search items..."
- /
- >
- {
- isPending
- &&
- <
- div className
- =
- "loading"
- >
- Filtering
- ...
- <
- /
- div
- >
- }
- <
- ul
- >
- {
- filteredItems
- .
- map
- (
- item
- =>
- (
- <
- li key
- =
- {
- item
- .
- id
- }
- >
- {
- item
- .
- name
- }
- <
- /
- li
- >
- )
- )
- }
- <
- /
- ul
- >
- <
- /
- div
- >
- )
- ;
- }
- useDeferredValue for Expensive UI
- import
- {
- useDeferredValue
- ,
- useMemo
- }
- from
- 'react'
- ;
- function
- DataGrid
- (
- {
- data
- }
- :
- {
- data
- :
- DataRow
- [
- ]
- }
- )
- {
- const
- [
- searchTerm
- ,
- setSearchTerm
- ]
- =
- useState
- (
- ''
- )
- ;
- const
- deferredSearchTerm
- =
- useDeferredValue
- (
- searchTerm
- )
- ;
- const
- filteredData
- =
- useMemo
- (
- (
- )
- =>
- {
- return
- data
- .
- filter
- (
- row
- =>
- Object
- .
- values
- (
- row
- )
- .
- some
- (
- value
- =>
- String
- (
- value
- )
- .
- toLowerCase
- (
- )
- .
- includes
- (
- deferredSearchTerm
- .
- toLowerCase
- (
- )
- )
- )
- )
- ;
- }
- ,
- [
- data
- ,
- deferredSearchTerm
- ]
- )
- ;
- return
- (
- <
- div
- >
- <
- input
- value
- =
- {
- searchTerm
- }
- onChange
- =
- {
- e
- =>
- setSearchTerm
- (
- e
- .
- target
- .
- value
- )
- }
- placeholder
- =
- "Search..."
- className
- =
- {
- searchTerm
- !==
- deferredSearchTerm
- ?
- 'stale'
- :
- ''
- }
- /
- >
- <
- DataGridRows
- data
- =
- {
- filteredData
- }
- isStale
- =
- {
- searchTerm
- !==
- deferredSearchTerm
- }
- /
- >
- <
- /
- div
- >
- )
- ;
- }
- Testing React 19 Features
- Testing Server Actions
- import
- {
- render
- ,
- screen
- ,
- fireEvent
- }
- from
- '@testing-library/react'
- ;
- import
- {
- jest
- }
- from
- '@jest/globals'
- ;
- import
- ContactForm
- from
- './ContactForm'
- ;
- // Mock server action
- const
- mockSubmitForm
- =
- jest
- .
- fn
- (
- )
- ;
- describe
- (
- 'ContactForm'
- ,
- (
- )
- =>
- {
- it
- (
- 'submits form with server action'
- ,
- async
- (
- )
- =>
- {
- render
- (
- <
- ContactForm
- /
- >
- )
- ;
- fireEvent
- .
- change
- (
- screen
- .
- getByLabelText
- (
- 'Email'
- )
- ,
- {
- target
- :
- {
- value
- :
- 'test@example.com'
- }
- }
- )
- ;
- fireEvent
- .
- click
- (
- screen
- .
- getByText
- (
- 'Submit'
- )
- )
- ;
- expect
- (
- mockSubmitForm
- )
- .
- toHaveBeenCalledWith
- (
- expect
- .
- any
- (
- FormData
- )
- )
- ;
- }
- )
- ;
- it
- (
- 'shows loading state during submission'
- ,
- async
- (
- )
- =>
- {
- mockSubmitForm
- .
- mockImplementation
- (
- (
- )
- =>
- new
- Promise
- (
- resolve
- =>
- setTimeout
- (
- resolve
- ,
- 100
- )
- )
- )
- ;
- render
- (
- <
- ContactForm
- /
- >
- )
- ;
- fireEvent
- .
- click
- (
- screen
- .
- getByText
- (
- 'Submit'
- )
- )
- ;
- expect
- (
- screen
- .
- getByText
- (
- 'Submitting...'
- )
- )
- .
- toBeInTheDocument
- (
- )
- ;
- await
- waitFor
- (
- (
- )
- =>
- {
- expect
- (
- screen
- .
- getByText
- (
- 'Submit'
- )
- )
- .
- toBeInTheDocument
- (
- )
- ;
- }
- )
- ;
- }
- )
- ;
- }
- )
- ;
- Testing Optimistic Updates
- import
- {
- render
- ,
- screen
- ,
- fireEvent
- ,
- waitFor
- }
- from
- '@testing-library/react'
- ;
- import
- {
- jest
- }
- from
- '@jest/globals'
- ;
- import
- TodoList
- from
- './TodoList'
- ;
- describe
- (
- 'useOptimistic'
- ,
- (
- )
- =>
- {
- it
- (
- 'shows optimistic update immediately'
- ,
- async
- (
- )
- =>
- {
- const
- mockAddTodo
- =
- jest
- .
- fn
- (
- (
- )
- =>
- new
- Promise
- (
- resolve
- =>
- setTimeout
- (
- resolve
- ,
- 100
- )
- )
- )
- ;
- render
- (
- <
- TodoList
- todos
- =
- {
- [
- ]
- }
- addTodo
- =
- {
- mockAddTodo
- }
- /
- >
- )
- ;
- fireEvent
- .
- change
- (
- screen
- .
- getByPlaceholderText
- (
- 'Add a todo'
- )
- ,
- {
- target
- :
- {
- value
- :
- 'New todo'
- }
- }
- )
- ;
- fireEvent
- .
- click
- (
- screen
- .
- getByText
- (
- 'Add'
- )
- )
- ;
- // Optimistic update appears immediately
- expect
- (
- screen
- .
- getByText
- (
- 'New todo'
- )
- )
- .
- toBeInTheDocument
- (
- )
- ;
- // Wait for actual submission
- await
- waitFor
- (
- (
- )
- =>
- {
- expect
- (
- mockAddTodo
- )
- .
- toHaveBeenCalledWith
- (
- {
- id
- :
- expect
- .
- any
- (
- Number
- )
- ,
- text
- :
- 'New todo'
- }
- )
- ;
- }
- )
- ;
- }
- )
- ;
- }
- )
- ;
- Performance Best Practices
- React Compiler Guidelines
- Write Standard React Code
-
- The compiler works best with idiomatic React patterns
- Avoid Manual Memoization
-
- Let the compiler handle useMemo, useCallback, and memo
- Keep Components Pure
-
- Avoid side effects in render
- Use Stable References
-
- Pass stable objects as props
- // Good: Clean, idiomatic React
- function
- ProductCard
- (
- {
- product
- ,
- onAddToCart
- }
- )
- {
- const
- [
- quantity
- ,
- setQuantity
- ]
- =
- useState
- (
- 1
- )
- ;
- const
- handleAdd
- =
- (
- )
- =>
- {
- onAddToCart
- (
- product
- .
- id
- ,
- quantity
- )
- ;
- }
- ;
- return
- (
- <
- div
- >
- <
- h3
- >
- {
- product
- .
- name
- }
- <
- /
- h3
- >
- <
- p
- >
- $
- {
- product
- .
- price
- }
- <
- /
- p
- >
- <
- input
- type
- =
- "number"
- value
- =
- {
- quantity
- }
- onChange
- =
- {
- e
- =>
- setQuantity
- (
- Number
- (
- e
- .
- target
- .
- value
- )
- )
- }
- min
- =
- "1"
- /
- >
- <
- button onClick
- =
- {
- handleAdd
- }
- >
- Add to Cart
- <
- /
- button
- >
- <
- /
- div
- >
- )
- ;
- }
- // Avoid: Manual optimization
- function
- ProductCard
- (
- {
- product
- ,
- onAddToCart
- }
- )
- {
- const
- [
- quantity
- ,
- setQuantity
- ]
- =
- useState
- (
- 1
- )
- ;
- const
- handleAdd
- =
- useCallback
- (
- (
- )
- =>
- {
- onAddToCart
- (
- product
- .
- id
- ,
- quantity
- )
- ;
- }
- ,
- [
- product
- .
- id
- ,
- quantity
- ,
- onAddToCart
- ]
- )
- ;
- return
- (
- <
- div
- >
- <
- h3
- >
- {
- product
- .
- name
- }
- <
- /
- h3
- >
- <
- p
- >
- $
- {
- product
- .
- price
- }
- <
- /
- p
- >
- <
- QuantityInput
- value
- =
- {
- quantity
- }
- onChange
- =
- {
- setQuantity
- }
- /
- >
- <
- button onClick
- =
- {
- handleAdd
- }
- >
- Add to Cart
- <
- /
- button
- >
- <
- /
- div
- >
- )
- ;
- }
- Server Components Best Practices
- Keep Server Components Server-Only
-
- No event handlers, hooks, or browser APIs
- Minimize Client Components
-
- Only use 'use client' when necessary
- Pass Data as Props
-
- Serialize data when passing from Server to Client
- Use Server Actions for Mutations
- Keep data operations on the server
// Good: Server Component for static content
async
function
ProductPage
(
{
id
}
:
{
id
:
string
}
)
{
const
product
=
await
fetchProduct
(
id
)
;
return
(
<
article
< header
< h1
{ product . name } < / h1
< p
{ product . description } < / p
< / header
< img src = { product . imageUrl } alt = { product . name } width = { 600 } height = { 400 } /
< PriceDisplay price = { product . price } /
< AddToCartForm productId = { product . id } /
< / article
) ; } // Client Component only for interactivity 'use client' ; function AddToCartForm ( { productId } : { productId : string } ) { const [ isAdding , setIsAdding ] = useState ( false ) ; async function handleSubmit ( ) { setIsAdding ( true ) ; await addToCart ( productId ) ; setIsAdding ( false ) ; } return ( < form action = { handleSubmit }
< button type = "submit" disabled = { isAdding }
{ isAdding ? 'Adding...' : 'Add to Cart' } < / button
< / form
) ; } Migration Guide From React 18 to 19 Update Dependencies : npm install react@19 react-dom@19 Adopt Server Components : Identify data-fetching components Remove client-side code from Server Components Add 'use client' directive where needed Replace Manual Optimistic Updates : // Before function TodoList ( { todos , addTodo } ) { const [ optimisticTodos , setOptimisticTodos ] = useState ( todos ) ; const handleAdd = async ( text ) => { const newTodo = { id : Date . now ( ) , text } ; setOptimisticTodos ( [ ... optimisticTodos , newTodo ] ) ; await addTodo ( newTodo ) ; } ; } // After function TodoList ( { todos , addTodo } ) { const [ optimisticTodos , addOptimisticTodo ] = useOptimistic ( todos , ( state , newTodo ) => [ ... state , newTodo ] ) ; const handleAdd = async ( formData ) => { const newTodo = { id : Date . now ( ) , text : formData . get ( 'text' ) } ; addOptimisticTodo ( newTodo ) ; await addTodo ( newTodo ) ; } ; } Enable React Compiler : Install babel-plugin-react-compiler Remove manual memoization Let the compiler optimize automatically References React 19 Official Docs: https://react.dev/blog/2024/04/25/react-19 React Server Components: https://react.dev/reference/rsc/server-components React Compiler: https://react.dev/learn/react-compiler TypeScript with React: https://react-typescript-cheatsheet.netlify.app/