-
Use immutable values everywhere.
-
Use
immerfor mutative operations. -
Almost always making types
readonly, including arrays. -
Almost always use pure functions.
-
Remember that local mutation is fine.
-
Make sure all code is strongly typed.
-
Use null coalescing operator(
??) and optional chaining(?.) to deal with undefined. -
Avoid type assertions(
as). Usezodinstead. -
Avoid
any. Usezodandtiny-invariantto assert assumptions and to fix type safety issues. -
When necessary use
unknowninstead ofany. -
anyis fine with generic constraints.
-
Almost always specify return types for functions, especially reusable functions.
-
Use
Iterable/IterableIteratorinstead of arrays wherever possible, especially as parameters and returning values from reusable functions. -
Remember generator functions return mutable Iterables.
-
Return arrays as mutable(
T[]) from reusable functions. -
Always return tuples as const(
readonly [Foo, Bar]). -
Prefer for-of loops over for loops. Use for-of(over
Object.keys) instead of for-in loop. Also, avoid `forEach``. -
Avoid invariants in library code.
-
Prefer using objects as parameters to functions instead of multiple parameters.
-
Prefer async/await to promise chaining with
then. -
Throw exceptions. Don't use
catch. -
Prefer
for await ofand async iterable where possible. -
Avoid floating promises.
-
Always handle promise errors at boundaries. Create and use a reusable layer for handling errors at boundaries.
-
Use React.ComponentProps type for HTML elements and React components.
-
Organize folders by feature(domain) instead of by type(components, hooks, routes etc).
-
Use Suspense. Use Loading components judiciously.
-
Use Error Boundaries. Use
react-error-boundary. Usereact-error-overlayfor dev. -
Make sure you have Suspense/ErrorBoundary components judiciously placed at strategic locations. Generally always use both at the same place.
-
Throw exceptions. Handle them appropriately at error boundaries, for example at the page level. Use
showBoundaryfunction fromreact-error-boundaryto throw exceptions from event handlers. -
Reusable components should be controlled components.
-
Prefer uncontrolled components for page-level components or 'app' components.
-
Always use
keyattribute for lists. -
Nearly never use
useEffect, if you do, always encapsulate it. -
Prefer event handlers for side effects, avoid
useEffect. -
Understand the advantages of colocation.
-
Don't use any
Javascript/Typescriptin JSX, except the following- Simple one-line lambda expressions.
- Conditional operators(&&, ||, ??) and ternary operator
- Higher order function
map.
-
Don't put a lot of
Javascript/Typescriptin components. Use hooks instead.
-
Avoid redux.
-
Avoid state as much as possible. Prefer storing state in
route, useloader/actionorreact-queryfor server state. -
Avoid
Context. -
Retain view state in components using
statefunction. -
If state transitions are as simple as updating a single field, use
useUpdateState. -
use
statefunction to avoid stale closures problems. In rare cases useuseEvent. -
Retain shared state in a parent component. Use
statefunction and do prop drilling. If necessary, usetreestate. -
For page state use URL, use
global-stateonly when necessary. -
You can use
Contextfor sharing rarely changing values like theme or creating an HTML-like API. Usetree. -
For global state use
global-statelibrary and signals. -
Store rarely changing config state in context.
-
Store auth state in context.
-
Avoid using useState. Use state function library or use useReducer
-
Use
useInt,useStringetc for storing one-off primitive values. -
Avoid
useEffectfor the synchronizing state. UseuseSignalinstead. -
Don't synchronize the state with props. Use
keyattribute instead.
-
Split components according to state, to avoid unnecessary renders.
-
Store state in the component close to where it is used.
-
Use
useMemofor expensive computations. -
Use
useEventfor callbacks where necessary. -
Use the
childrentrick to avoid rerenders when necessary. -
Prefer
lazyloading pages. -
Use virtual lists for rendering large lists.
-
Split actions and values in separate contexts.
-
Use
useDeferredEventfor transitions or avoid flickering due toSuspense. UseuseDeferredValuewhen necessary.
-
Manipulate server state with either
@tanstack/react-queryorreact-router`sloaderandaction. -
Identify queries and mutations cohesion.
-
Don't fetch in top-level components unless avoiding waterfalls is necessary. Always fetch exactly where needed.
-
Try fixing waterfalls with react-router's 'loader' and/or react-query's
useQueries. -
If possible, centralize query objects passed to react-query.
-
Identify all queries affected by mutations and invalidate them.
-
When using mutations, consider optimistic updates.
-
Use
useTransition/useDeferredEventand loading indicators withisPending. -
Use toasts for failed optimistic mutations.
-
Prefer pagination to infinite scrolling.
-
Use
zodfor all API call responses. -
Use
zodfor pathnames, search params, local storage etc. -
Use
zodfor form validation. -
Prefer
formHTML element for API mutations. -
Use
zodto parseFormData.
-
Avoid margin. Consider using
gapmore often. -
Use semantic HTML where possible.
-
Avoid using Tailwind’s square bracket notation as much as possible.
-
Avoid using specific color values.
-
Avoid png files.
-
Do not inline
svgcode. -
Use flexbox components for components and simple layouts.
-
Use css grid for page layouts or complex layouts.
-
Use
cn(clsx) for conditional styling. -
Allow overriding styles using
classNamefor reusable components. -
Consider using
cvafor creating reusable components with lot's of variations. -
Make sure you split your components similar to how you split your functions.
-
Use
uilibrary for at least form controls, date picker, Card, Dialog etc, wherever accessibility is important.
-
Follow playwright best practices.
-
Use locator.fill instead of locator.type.