
React19 Concurrent Patterns
Migrate form and async UI from React 18 useReducer patterns to React 19 Actions, useActionState, and pending states without reinventing loading/error handling.
Overview
React 19 Actions Pattern Reference is an agent skill for the Build phase that explains Actions, useActionState, and async form handling as the React 19 replacement for useReducer-heavy client forms.
Install
npx skills add https://github.com/github/awesome-copilot --skill react19-concurrent-patternsWhat is this skill?
- Documents React 19 Actions vs React 18 useReducer + useEffect form flows
- Covers useActionState with built-in pending state and error handling
- Explains form submit and button-click Action wiring
- Supports Server Component direct server mutation patterns
- Reference for optimistic updates alongside async Actions
Adoption & trust: 719 installs on skills.sh; 34.6k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You are still wiring forms with useReducer, manual loading flags, and try/catch dispatch loops when React 19 already bundles that behavior into Actions.
Who is it for?
Solo builders on React 19 (or migrating) who want a concise pattern guide for forms and async client mutations.
Skip if: Teams that need a full component library scaffold, non-React stacks, or backend-only API design with no UI layer.
When should I use this skill?
You are implementing or migrating React forms and async UI to React 19 Actions or useActionState.
What do I get? / Deliverables
Your agent implements React 19 Action-based forms with consistent pending and error UI and fewer custom state machines.
- Action-based form implementations aligned to the reference patterns
- Reduced custom reducer logic for submit flows
Recommended Skills
Journey fit
Concurrent Actions and useActionState are implemented during product UI work in the build phase, before ship-time testing. Canonical shelf is frontend because the skill documents client and Server Component mutation patterns, not backend APIs or launch copy.
How it compares
Use as a focused React 19 pattern reference instead of generic “modern React” chat snippets that omit useActionState semantics.
Common Questions / FAQ
Who is react19-concurrent-patterns for?
Indie and solo frontend developers using React who need accurate React 19 Actions and useActionState guidance while building or refactoring forms.
When should I use react19-concurrent-patterns?
During the build phase when implementing or migrating forms, async submits, and optimistic UI; also when validating that Server Component mutation flows match React 19 docs.
Is react19-concurrent-patterns safe to install?
It is documentation-style procedural knowledge with no bundled executables; review the Security Audits panel on this Prism page before installing any skill from the catalog.
SKILL.md
READMESKILL.md - React19 Concurrent Patterns
# React 19 Actions Pattern Reference React 19 introduces **Actions** a pattern for handling async operations (like form submissions) with built-in loading states, error handling, and optimistic updates. This replaces the `useReducer + state` pattern with a simpler API. ## What are Actions? An **Action** is an async function that: - Can be called automatically when a form submits or button clicks - Runs with automatic loading/pending state - Updates the UI automatically when done - Works with Server Components for direct server mutation --- ## useActionState() `useActionState` is the client-side Action hook. It replaces `useReducer + useEffect` for form handling. ### React 18 Pattern ```jsx // React 18 form with useReducer + state: function Form() { const [state, dispatch] = useReducer( (state, action) => { switch (action.type) { case 'loading': return { ...state, loading: true, error: null }; case 'success': return { ...state, loading: false, data: action.data }; case 'error': return { ...state, loading: false, error: action.error }; } }, { loading: false, data: null, error: null } ); async function handleSubmit(e) { e.preventDefault(); dispatch({ type: 'loading' }); try { const result = await submitForm(new FormData(e.target)); dispatch({ type: 'success', data: result }); } catch (err) { dispatch({ type: 'error', error: err.message }); } } return ( <form onSubmit={handleSubmit}> <input name="email" /> {state.loading && <Spinner />} {state.error && <Error msg={state.error} />} {state.data && <Success data={state.data} />} <button disabled={state.loading}>Submit</button> </form> ); } ``` ### React 19 useActionState() Pattern ```jsx // React 19 same form with useActionState: import { useActionState } from 'react'; async function submitFormAction(prevState, formData) { // prevState = previous return value from this function // formData = FormData from <form action={submitFormAction}> try { const result = await submitForm(formData); return { data: result, error: null }; } catch (err) { return { data: null, error: err.message }; } } function Form() { const [state, formAction, isPending] = useActionState( submitFormAction, { data: null, error: null } // initial state ); return ( <form action={formAction}> <input name="email" /> {isPending && <Spinner />} {state.error && <Error msg={state.error} />} {state.data && <Success data={state.data} />} <button disabled={isPending}>Submit</button> </form> ); } ``` **Differences:** - One hook instead of `useReducer` + logic - `formAction` replaces `onSubmit`, form automatically collects FormData - `isPending` is a boolean, no dispatch calls - Action function receives `(prevState, formData)` --- ## useFormStatus() `useFormStatus` is a **child component hook** that reads the pending state from the nearest form. It acts like a built-in `isPending` signal without prop drilling. ```jsx // React 18 must pass isPending as prop: function SubmitButton({ isPending }) { return <button disabled={isPending}>Submit</button>; } function Form({ isPending, formAction }) { return ( <form action={formAction}> <input /> <SubmitButton isPending={isPending} /> </form> ); } // React 19 useFormStatus reads it automatically: function SubmitButton() { const { pending } = useFormStatus(); return <button disabled={pending}>Submit</button>; } function Form() { const [state, formAction] = useActionState(submitFormAction, {}); return ( <form action={formAction}> <input /> <SubmitButton /> {/* No prop needed */} </form> ); } ``` **Key point:** `useFormStatus` only works inside a `<form action={...}>` regular `<form onSubmit>` won't trigger