React TypeScript
Type-safe React = compile-time guarantees = confident refactoring.
; } & React . ComponentPropsWithoutRef < 'button'
; function Button ( { ref , children , ... props } : ButtonProps ) { return < button ref = { ref } { ... props }
{ children } < / button
; } useActionState - replaces useFormState: import { useActionState } from 'react' ; type FormState = { errors ? : string [ ] ; success ? : boolean } ; function Form ( ) { const [ state , formAction , isPending ] = useActionState ( submitAction , { } ) ; return < form action = { formAction }
... < / form
; } use() - unwraps promises/context: function UserProfile ( { userPromise } : { userPromise : Promise < User
} ) { const user = use ( userPromise ) ; // Suspends until resolved return < div
{ user . name } < / div
; } See react-19-patterns.md for useOptimistic, useTransition, migration checklist.
Props - extend native elements: type ButtonProps = { variant : 'primary' | 'secondary' ; } & React . ComponentPropsWithoutRef < 'button' ; function Button ( { variant , children , ... props } : ButtonProps ) { return < button className = { variant } { ... props }
{ children } < / button
; } Children typing : type Props = { children : React . ReactNode ; // Anything renderable icon : React . ReactElement ; // Single element render : ( data : T ) => React . ReactNode ; // Render prop } ; Discriminated unions for variant props: type ButtonProps = | { variant : 'link' ; href : string } | { variant : 'button' ; onClick : ( ) => void } ; function Button ( props : ButtonProps ) { if ( props . variant === 'link' ) { return < a href = { props . href }
Link < / a
; } return < button onClick = { props . onClick }
Button < / button
; }
Use specific event types for accurate target typing: // Mouse function handleClick ( e : React . MouseEvent < HTMLButtonElement ) { e . currentTarget . disabled = true ; } // Form function handleSubmit ( e : React . FormEvent < HTMLFormElement
) { e . preventDefault ( ) ; const formData = new FormData ( e . currentTarget ) ; } // Input function handleChange ( e : React . ChangeEvent < HTMLInputElement
) { console . log ( e . target . value ) ; } // Keyboard function handleKeyDown ( e : React . KeyboardEvent < HTMLInputElement
) { if ( e . key === 'Enter' ) e . currentTarget . blur ( ) ; } See event-handlers.md for focus, drag, clipboard, touch, wheel events.
useState - explicit for unions/null: const [ user , setUser ] = useState < User | null ( null ) ; const [ status , setStatus ] = useState < 'idle' | 'loading'
( 'idle' ) ; useRef - null for DOM, value for mutable: const inputRef = useRef < HTMLInputElement
( null ) ; // DOM - use ?. const countRef = useRef < number
( 0 ) ; // Mutable - direct access useReducer - discriminated unions for actions: type Action = | { type : 'increment' } | { type : 'set' ; payload : number } ; function reducer ( state : State , action : Action ) : State { switch ( action . type ) { case 'set' : return { ... state , count : action . payload } ; default : return state ; } } Custom hooks - tuple returns with as const: function useToggle ( initial = false ) { const [ value , setValue ] = useState ( initial ) ; const toggle = ( ) => setValue ( v => ! v ) ; return [ value , toggle ] as const ; } useContext - null guard pattern: const UserContext = createContext < User | null
( null ) ; function useUser ( ) { const user = useContext ( UserContext ) ; if ( ! user ) throw new Error ( 'useUser outside UserProvider' ) ; return user ; } See hooks.md for useCallback, useMemo, useImperativeHandle, useSyncExternalStore.
Generic components infer types from props - no manual annotations at call site. Pattern - keyof T for column keys, render props for custom rendering: type Column < T = { key : keyof T ; header : string ; render ? : ( value : T [ keyof T ] , item : T ) => React . ReactNode ; } ; type TableProps < T
= { data : T [ ] ; columns : Column < T
[ ] ; keyExtractor : ( item : T ) => string | number ; } ; function Table < T
( { data , columns , keyExtractor } : TableProps < T
) { return ( < table
< thead
< tr
{ columns . map ( col => < th key = { String ( col . key ) }
{ col . header } < / th
) } < / tr
< / thead
< tbody
{ data . map ( item => ( < tr key = { keyExtractor ( item ) }
{ columns . map ( col => ( < td key = { String ( col . key ) }
{ col . render ? col . render ( item [ col . key ] , item ) : String ( item [ col . key ] ) } < / td
) ) } < / tr
) ) } < / tbody
< / table
) ; } Constrained generics for required properties: type HasId = { id : string | number } ; function List < T extends HasId
( { items } : { items : T [ ] } ) { return < ul
{ items . map ( item => < li key = { item . id }
... < / li
) } < / ul
; } See generic-components.md for Select, List, Modal, FormField patterns.
React 19 Server Components run on server, can be async. Async data fetching : export default async function UserPage ( { params } : { params : { id : string } } ) { const user = await fetchUser ( params . id ) ; return < div { user . name } < / div
; } Server Actions - 'use server' for mutations: 'use server' ; export async function updateUser ( userId : string , formData : FormData ) { await db . user . update ( { where : { id : userId } , data : { ... } } ) ; revalidatePath (
/users/ ${ userId }) ; } Client + Server Action : 'use client' ; import { useActionState } from 'react' ; import { updateUser } from '@/actions/user' ; function UserForm ( { userId } : { userId : string } ) { const [ state , formAction , isPending ] = useActionState ( ( prev , formData ) => updateUser ( userId , formData ) , { } ) ; return < form action = { formAction }... < / form
; } use() for promise handoff : // Server: pass promise without await async function Page ( ) { const userPromise = fetchUser ( '123' ) ; return < UserProfile userPromise = { userPromise } /
; } // Client: unwrap with use() 'use client' ; function UserProfile ( { userPromise } : { userPromise : Promise < User
} ) { const user = use ( userPromise ) ; return < div
{ user . name } < / div
; } See server-components.md for parallel fetching, streaming, error boundaries. Both TanStack Router and React Router v7 provide type-safe routing solutions. TanStack Router - Compile-time type safety with Zod validation: import { createRoute } from '@tanstack/react-router' ; import { z } from 'zod' ; const userRoute = createRoute ( { path : '/users/$userId' , component : UserPage , loader : async ( { params } ) => ( { user : await fetchUser ( params . userId ) } ) , validateSearch : z . object ( { tab : z . enum ( [ 'profile' , 'settings' ] ) . optional ( ) , page : z . number ( ) . int ( ) . positive ( ) . default ( 1 ) , } ) , } ) ; function UserPage ( ) { const { user } = useLoaderData ( { from : userRoute . id } ) ; const { tab , page } = useSearch ( { from : userRoute . id } ) ; const { userId } = useParams ( { from : userRoute . id } ) ; } React Router v7 - Automatic type generation with Framework Mode: import type { Route } from "./+types/user" ; export async function loader ( { params } : Route . LoaderArgs ) { return { user : await fetchUser ( params . userId ) } ; } export default function UserPage ( { loaderData } : Route . ComponentProps ) { const { user } = loaderData ; // Typed from loader return < h1
{ user . name } < / h1
; } See tanstack-router.md for TanStack patterns and react-router.md for React Router patterns. ALWAYS: Specific event types (MouseEvent, ChangeEvent, etc) Explicit useState for unions/null ComponentPropsWithoutRef for native element extension Discriminated unions for variant props as const for tuple returns ref as prop in React 19 (no forwardRef) useActionState for form actions Type-safe routing patterns (see routing section) NEVER: any for event handlers JSX.Element for children (use ReactNode) forwardRef in React 19+ useFormState (deprecated) Forget null handling for DOM refs Mix Server/Client components in same file Await promises when passing to use() hooks.md - useState, useRef, useReducer, useContext, custom hooks event-handlers.md - all event types, generic handlers react-19-patterns.md - useActionState, use(), useOptimistic, migration generic-components.md - Table, Select, List, Modal patterns server-components.md - async components, Server Actions, streaming tanstack-router.md - TanStack Router typed routes, search params, navigation react-router.md - React Router v7 loaders, actions, type generation, forms