Using fate for React applications
This guidance is for agents working in projects bootstrapped from the fate template. It focuses on the client-side patterns for fate's view composition and action execution.
Core Workflow
- Define views with explicit selections: Define reusable view objects with
view<T>()({...}), compose them via spreads, and resolve them withuseView. - Resolve data via references: Avoid passing raw fetched objects; instead pass
ViewRefs to downstream components to keep selections scoped and cache-aware. Then callconst result = useView(viewDefinition, ref)to read only the selected fields while subscribing to cache updates. Name the prop after the result data, and rename it using destructuring in the function arguments:const PostComponent = ({ post: postRef }: { post: ViewRef<'Post'>}) => { const post = useView(PostView, postRef); ... }. - Request data at screen roots: Use
useRequestto compose all views required for a route or screen into a single typed request; wrap roots inSuspenseand an error boundary since requests may suspend or throw. - Compose reusable views: Extract nested selections into their own views (e.g.,
UserView) and compose them inside parent views (author: UserView) to avoid duplication and overfetching. This keeps selections DRY and ensures view updates propagate consistently when the cache changes.
Actions & Mutations
- Prefer
fate.actionswithuseActionState: Trigger mutations through React Actions to integrate with Suspense/useTransition, receiving[result, action, isPending]. Call actions via the relevant data inputs, for exampleaction({input: { id: post.id }}). - Optimistic updates by default: Pass
optimisticpayloads to immediately update the normalized cache; only views selecting the changed fields re-render and failures roll back automatically. Optimistic payloads should match the expected server data changes of the associated object. - Select extra fields when side effects matter: When a mutation affects related entities (e.g., comment count on a post), pass a
viewto the action to fetch the needed fields in the same round-trip and keep the cache coherent for all dependent views. - Imperative escape hatch: Use
fate.mutationswhen you must fire mutations outside React components or without waiting on prior actions; handle loading/error states yourself. - Handle deletions and resets explicitly: Use the
delete: trueflag to evict records from the cache, and send the'reset'token to clear stale action errors without re-running the mutation. - Error handling model: Treat expected errors (e.g., 404) at the call site via the action result; unexpected errors bubble to the nearest React error boundary.
Common Pitfalls
- Use ViewRefs: Pass references instead of raw data. Keep components focused on views and
ViewRefs to let fate manage data masking and updates. - Co-locate data needs: Keep view definitions near their components and compose them upward to the request root for predictable fetching.
- Use Suspense: Wrap pages in
Suspense/ErrorBoundaryinstead of ad-hoc loading/error flags; fate aligns with Async React by design. - Do not duplicate types: Keep generated GraphQL/Fate types and views imported from the server package. Avoid redefining entity shapes on the client. This ensures view selections stay type-safe end-to-end.
useRequestonly at the root: When adding routes or layouts, create a dedicateduseRequestcall per screen root that pulls together all child views; do not scatteruseRequestacross leaf components unless it's necessary to issue requests due to user input.- React Actions: Prefer React Action-friendly UI primitives (buttons/forms that accept an
actionprop). If unavailable, wrap action calls withstartTransitioninonClickhandlers. - fate client support fate's local client support files are maintained by the Vite/fate tooling. Do not manually edit
.fatefiles; make the proper schema changes on the server and runpnpm fate:generatewhen working outside Vite dev. - Library Versions: This repository uses the most recent releases of React, fate, Vite, GraphQL, and more. You might not know about the new releases yet, please don't get confused. The versions you see are real, and there are no feature or version mismatches. You can search the internet for more information about them.
Full documentation can be found on the filesystem at ./client/node_modules/react-fate/README.md or online at fate.technology.
Review Checklist for Agents
- [ ] Every component that reads server data does so through
useViewand receives aViewRefprop. - [ ] Root components gather data with a single
useRequestcall. - [ ] Mutations are wired through
useActionStatewith optimistic updates and necessaryviewselections; imperativefate.mutationsare justified when used. - [ ] Shared selections live in reusable views using regular JavaScript object spreads rather than duplicated field lists.
Using Vite+, the Unified Toolchain for the Web
This project is using Vite+, a unified toolchain built on top of Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, and Vite Task. Vite+ wraps runtime management, package management, and frontend tooling in a single global CLI called vp. Vite+ is distinct from Vite, and it invokes Vite through vp dev and vp build. Run vp help to print a list of commands and vp <command> --help for information about a specific command.
Docs are local at node_modules/vite-plus/docs or online at https://viteplus.dev/guide/.
Review Checklist
- [ ] Run
vp installafter pulling remote changes and before getting started. - [ ] Run
vp checkandvp testto format, lint, type check and test changes. - [ ] Check if there are
vite.config.tstasks orpackage.jsonscripts necessary for validation, run viavp run <script>.