React.js Frontend Web Developer | Next.js Specialist
Full-time Frontend Engineer roles (React/Next.js/TypeScript), freelance projects, and nerding out about TanStack Query, WebSockets, or Next.js SSR with fellow devs.
the discriminated-union transfer state is the right instinct, and the honest framing on near-real-time polling over pretending it's a stream is exactly the kind of clarity that avoids a whole class of confused bug reports later. one thing I'd add to the money-as-floating-point section: even after moving to minor-unit integers, currency conversion/display formatting still needs its own decimal library at the boundary, Intl.NumberFormat is fine for display but silently reintroduces float rounding if you feed it a computed value instead of a stored one.
the pattern you're describing (passing server children through a client shell) also matters for props, not just component placement: anything crossing that 'use client' boundary has to be RSC-serializable, so a function or a class-instance prop silently breaks. worth calling out since the fix looks identical to the composition pattern but the actual bug is serialization, not the boundary itself.
The agent-state-as-first-class-concept point maps almost exactly onto a state machine, planning, executing, waiting-for-approval, completed, needs-intervention are states with explicit valid transitions, not just a status string you branch on. Something like XState models this well since it makes illegal transitions (like completing from idle without executing) impossible by construction, instead of relying on every consumer of the state to check it correctly.
Solid writeup on the selector granularity, that's the part people miss when they first move off Context. One gap worth flagging: the persist middleware and Next.js SSR don't mix cleanly out of the box, the server has no localStorage so the first render always uses the default state, then the client rehydrates a second later. If the persisted cart count shows up in the UI, that's a visible flash or a hydration mismatch warning unless you gate the read behind a mounted check.
Good breakdown. One precision point on Fix 1: Google's rendering service does execute JS and will pick up late-injected title and meta from react-helmet-async, it is just delayed and subject to crawl budget. The crawlers that genuinely never run JS are Bing, the social unfurlers (Slack, LinkedIn, Facebook, X) and most AI search bots, so that is the real reason to get tags into the initial HTML, not "Google can't see it." On the prerender snapshot approach, watch staleness, a build-time snapshot bakes the price into the JSON-LD as of that build, and on a 400-page catalog with moving prices Rich Results starts flagging price mismatch against the live page unless you rebuild on data change or move to ISR. Also worth adding lastmod to the sitemap, loc alone gives Google almost nothing to prioritise recrawls with.
The enum-over-boolean-flags swap is the strongest part, isSmall plus isLarge lets you represent both true which is meaningless. Two things on the clean version though. Making the whole card clickable by putting onClick on the div is a step back from a real button for a11y and keyboard, and since actionSlot renders inside that div, clicking Edit or Delete also fires onSelect unless you stopPropagation, which is easy to miss. And fully flattening the domain model works for three fields but if the card needs eight you have traded one prop for a wide order-sensitive call site. The middle ground is a narrow view-model type the card owns and the parent maps into, you get the decoupling from UserDomainModel without the prop explosion.
Solid writeup. Two gaps worth closing on that setup. Razorpay retries webhooks on any non-2xx or timeout and can deliver the same event more than once, so the handler has to be idempotent, dedupe on the payment id or event id with a unique constraint, otherwise a retry double-upgrades the account. Also return 200 fast and do the Supabase write async, the webhook times out in a few seconds and a slow write under load just triggers more retries. Second, slowapi is in-memory, so the moment you run more than one Render instance or more than one uvicorn worker each process keeps its own counter and your real limit is Nx what you set, plus it resets on every deploy. For that to actually hold you need a shared store like Redis or rate limiting at the edge.
Good split on the two endpoints. Two things worth adding on the POST side. Even once the hidden fields are right, CF7 can return 200 with mail_sent false and status spam, so a headless form that only checks the HTTP status looks like it worked while nothing got delivered. You have to read the status field in the JSON body. And if reCAPTCHA v3 is on the WP side, it scores every headless submit with no token and quietly drops them, so you either exclude the API path or generate and pass a token. On the simpler architecture, instead of an iframe you can put a Next.js route handler in front that proxies the POST server to server, that kills CORS, lets you inject the _wpcf7 fields and the recaptcha token in one place, and keeps your own markup.