agentsclimarketplace

Way typescript react style

Skill way-platform/skills/way-typescript-react-style

Re-usable skills for agents at Way

Install
npx -y skills add way-platform/skills --skill way-typescript-react-style

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Guide for writing idiomatic, high-quality TypeScript and React code. Use this skill when writing, refactoring, or reviewing TypeScript/React code to ensure adherence to established conventions, React rules, and performance best practices. Trigger proactively when writing React components, TypeScript types, or Next.js pages.

SKILL.md

13.2 KB, as published. Nobody here has run it

Way TypeScript & React Style

Way Specific Conventions

  • Define reusable data/logic helpers as top-level pure functions (outside React components); keep component bodies focused on rendering and wiring
  • Complex predicates for array methods must be separate named functions
  • Prefer declarative over imperative — derive values during render rather than storing them in let/useState; use map/filter/reduce expressions rather than loops that push into an array; use object/array spread to produce new values rather than mutating existing ones
  • Always use early returnsif (isLoading) return <Spinner />; at the top, not let content with if/else branches
  • Never use IIFEs — extract a named function instead: a top-level pure function for derived values, or a named inner function for JSX that closes over component state
  • Avoid as casts; prefer narrowing, generics, or splitting code paths. Use as only when interoperating with external/untyped APIs and add a short justification comment
  • Handle undefined args explicitly (no ?? 0 for mandatory params)
  • Do NOT use useMemo or useCallback — React Compiler handles memoization. (Project AGENTS.md may define narrow exceptions.)
  • STOP before writing useEffect — most uses are wrong. Evaluate the decision tree in this skill first. Effects are only for synchronizing with external systems.
  • Data fetching hierarchy: Always use @connectrpc/connect-query hooks with generated *_connectquery.ts files. Only fall back to raw @tanstack/react-query when no generated file exists or the pattern cannot be expressed via connect-query.
  • Hydration: Never render server-side content that depends on client state (timestamps, window checks)

STOP — Before Writing useEffect

Effects are an escape hatch for synchronizing with external systems — if no external system is involved, you almost certainly don't need one.

Decision tree

About to write useEffect?
│
├─ Responding to a user action (click, submit, drag)?
│  └─ Use an EVENT HANDLER — you know exactly what happened
│
├─ Deriving a value from props or state?
│  └─ COMPUTE DURING RENDER — const fullName = first + ' ' + last
│
├─ Need to reset state when a prop changes?
│  └─ Use the KEY PROP — <Profile key={userId} />
│
├─ Filtering/transforming data?
│  └─ COMPUTE DURING RENDER (useMemo if expensive)
│
├─ Notifying a parent of a state change?
│  └─ Call the callback IN THE SAME EVENT HANDLER
│
├─ Chaining multiple state updates?
│  └─ Calculate ALL next state IN THE EVENT HANDLER
│
└─ Synchronizing with a non-React system (DOM API, WebSocket, browser API)?
   └─ This is the ONE case where useEffect is correct
      └─ Always include cleanup. For subscriptions, prefer useSyncExternalStore.

The top anti-patterns

Anti-patternWhat to do instead
useState + useEffect to derive a valueCompute during render: const x = a + b
useEffect to filter/transform dataCompute during render (or useMemo)
useEffect to reset state on prop changekey prop on the component
useEffect to respond to a click/submitPut the logic in the event handler
useEffect calling parent's onChangeCall onChange in the event handler
Chain of Effects triggering each otherCompute all state changes in one event handler
useEffect fetching without cleanupAdd let ignore = false cleanup (or use useQuery)
App init in useEffect([], ...)Module-level let didInit = false guard

For detailed examples with incorrect/correct code, see:


STOP — Before Writing Data Fetching Hooks

The most common mistake is reaching for raw @tanstack/react-query hooks when generated @connectrpc/connect-query hooks should be used instead. This causes redundant boilerplate, breaks transport injection, and bypasses the project's auth/error-handling layer.

The hierarchy (follow in order)

Fetching data from a ConnectRPC service?
│
├─ Is there a generated *_connectquery.ts file for this method?
│  └─ YES → Use @connectrpc/connect-query hooks ← DEFAULT
│     import { useQuery, useMutation } from "@connectrpc/connect-query"
│     import { listThings } from "@/gen/proto/.../*_connectquery"
│     const { data } = useQuery(listThings, { orgPath }, { transport })
│
└─ NO generated file, or need a pattern connect-query can't express?
   └─ Fall back to @tanstack/react-query directly
      import { useQuery } from "@tanstack/react-query"
      (custom queryFn, useInfiniteQuery with complex pagination, etc.)

connect-query patterns

// READ — useQuery
import { useQuery } from "@connectrpc/connect-query";
import { listThings } from "@/gen/proto/.../things_service-ThingsService_connectquery";
const transport = useClientConfig();
const { data, isLoading } = useQuery(listThings, { orgPath }, { transport });

// Conditional skip — use skipToken (NOT enabled: false)
import { skipToken } from "@tanstack/react-query";
const { data } = useQuery(listThings, orgPath ? { orgPath } : skipToken, { transport });

// WRITE — useMutation
import { useMutation } from "@connectrpc/connect-query";
const { mutate, isPending } = useMutation(deleteThing, {
  transport,
  onError: (error) => toastApiError(error, t("deleteFailed")),
});

// Cache invalidation after mutation
import { createConnectQueryKey } from "@connectrpc/connect-query";
const queryClient = useQueryClient();
queryClient.invalidateQueries({
  queryKey: createConnectQueryKey({ schema: listThings, transport, cardinality: "finite" }),
});

When raw tanstack-query IS appropriate

  • Non-RPC fetches (REST endpoints, third-party APIs)
  • Complex multi-RPC queryFn that composes several calls imperatively
  • useSuspenseQuery / useSuspenseInfiniteQuery if not yet supported by connect-query
  • When you need queryOptions() helper for SSR prefetching patterns

For the full tanstack-query API reference see references/tanstack-query.md.


TypeScript Style

Types

  • Never use boxed types (Number, String, Boolean, Symbol, Object). Use the lowercase primitives (number, string, boolean, symbol, object).
  • Never use any unless migrating from JavaScript. Use unknown for truly unknown values.
  • Never have unused type parameters in generics.

Callbacks

  • Return void, not any, for callbacks whose return value is ignored. void prevents accidental use of the return value.
  • Don't make callback parameters optional — callers can always pass a function that accepts fewer arguments.
  • Don't overload on callback arity — write a single overload with the maximum arity.

Type Safety in Practice

  • Narrow, don't cast — use type guards, discriminated unions, and satisfies instead of as.
  • Prefer unknown over any — force explicit narrowing at the call site.
  • Use satisfies to validate a value matches a type without widening it: const config = { ... } satisfies Config.
  • Prefer readonly for data that shouldn't be mutated (function parameters, returned arrays).
  • Avoid enums in application code — use as const objects or string literal unions. (Generated proto enums are the exception.)

React Rules

These are rules, not guidelines — breaking them causes bugs. Condensed from the official Rules of React.

1. Components & Hooks Must Be Pure

  • Idempotent: Same inputs (props, state, context) must produce the same output. Don't use new Date() or Math.random() during render — move them to effects or event handlers.
  • No side effects in render: React can render components multiple times. Side effects belong in event handlers or useEffect.
  • Local mutation is fine: Creating and modifying local variables during render is OK — the mutation isn't remembered between renders.
  • Props, state, and hook return values are immutable: Never mutate them directly. Use setter functions.
  • Don't mutate values after passing to JSX: React may evaluate JSX eagerly; mutating after the fact leads to stale UI.

2. React Controls Invocation

  • Never call component functions directly — use them in JSX. React needs to orchestrate rendering, reconciliation, and state management.
  • Never pass hooks around as values — always call hooks inline. Don't dynamically mutate or inject hooks.

3. Rules of Hooks

  • Top level only — no hooks in loops, conditions, nested functions, try/catch, or after early returns.
  • React functions only — call hooks from function components or custom hooks, never from regular functions.

React & Next.js Performance

See references/vercel-rules/ for the full 57-rule reference. Key patterns:

Eliminating Waterfalls

  • Move await into the branch that uses it — don't block early returns with unneeded fetches
  • Promise.all() for independent operations; start promises early, await late
  • Wrap slow data components in <Suspense> so the surrounding layout renders immediately
  • Sibling async Server Components fetch in parallel; a parent that awaits before rendering children creates a waterfall

Bundle Size

  • Import directly from source files, not barrel index.ts re-exports
  • next/dynamic for heavy components (maps, editors, charts)
  • Defer analytics/logging with dynamic(() => ..., { ssr: false })
  • Trigger import() on hover/focus to preload before click

Server-Side Performance

  • Verify auth inside every Server Action — don't rely on middleware alone
  • Pass only the fields a client component actually uses (no 50-field objects)
  • Sibling async Server Components fetch in parallel; awaiting in a parent creates a waterfall
  • React.cache() deduplicates calls within one request (avoid inline object args)
  • after() for logging/analytics so it doesn't block the response

Re-render Optimization

  • Derive values during render — don't store computed state or sync via effects
  • setState(prev => ...) when new state depends on previous value
  • useState(() => expensiveInit()) — function form, not expression
  • Depend on user.id not user; derive booleans and depend on those
  • useRef for transient values (mouse position, timers, flags)
  • startTransition for non-urgent updates

Rendering Performance

  • Explicit conditional rendering: Use ternary (condition ? <A /> : null), not &&, when the condition could be 0 or NaN.
  • Animate SVG wrappers: Wrap SVG in a <div> and animate the wrapper for GPU acceleration.
  • content-visibility: auto: Defer off-screen rendering for long lists.
  • Hydration mismatches: Use suppressHydrationWarning only for expected differences (timestamps, locales). For client-only data (theme), use an inline <script> to set the DOM before hydration.

JavaScript Performance

  • Set/Map for repeated lookups instead of array scans
  • toSorted() / toReversed() / toSpliced().sort() mutates
  • Combine multiple .filter()/.map() passes into one loop
  • Hoist RegExp to module scope; cache localStorage reads in a module-level Map

Advanced Patterns

  • Initialize once per app load: Don't put one-time init in useEffect([], ...) — it re-runs on remount and runs twice in dev. Use a module-level let didInit = false guard.
  • useEffectEvent: Creates a stable function reference that always calls the latest handler, without adding the handler to effect dependencies.

Available References

Detailed documentation available in the references/ directory:

  • useEffect Anti-Patterns: 9 common mistakes with incorrect/correct code examples.
  • useEffect Alternatives: useMemo, key prop, lifting state, useSyncExternalStore, event handlers, custom hooks.
  • TypeScript Do's and Don'ts: Official TypeScript handbook guidance on types, callbacks, and overloads.
  • Rules of React: All four pages of React's official rules — purity, invocation, hooks.
  • Vercel React Rules: 57 individual rule files with incorrect/correct code examples. Named by category prefix: async-*, bundle-*, server-*, client-*, rerender-*, rendering-*, js-*, advanced-*. Load individual files on demand rather than the full set.
  • TanStack Query Reference: Full API reference for @tanstack/react-query v5 — for fallback cases where connect-query is insufficient.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.