Genie React
Workflows

Loading and error states

Hold brief branches still, verify them, and restore the app.

These examples use two demo apps with different bridge ports. Run the setup linked in each section first. Keep each Genie binding aligned with its browser session.

Query loading

Use the bound TanStack Start session from Measure the loading-state height gap. Hold its known query, then prove the cache and visible branch agree:

export GENIE_BRIDGE_URL=ws://localhost:3000/__genie/ws
export GENIE_SESSION=docs-case-start
npx @genie-react/cli call query_simulate_state \
  '{"queryKey":["dashboard","metrics"],"state":"pending"}'
query_state="$(npx @genie-react/cli call query_get \
  '{"queryKey":["dashboard","metrics"]}' --json)"
printf '%s\n' "$query_state" | \
  jq -e '.status == "pending" and .simulatedState == "pending"'
agent-browser --session docs-case-start get text body | rg -F 'Loading metrics…'

Suspense fallback

Use the bound Router session from Verify routes and forced states. Navigate, discover the current component ID, force the fallback, then join React evidence to visible text:

export GENIE_BRIDGE_URL=ws://localhost:3030/__genie/ws
export GENIE_SESSION=docs-case-router
npx @genie-react/cli call router_navigate '{"to":"/suspense"}'
npx @genie-react/cli call devtools_wait \
  '{"condition":"navigation","name":"/suspense","timeoutMs":5000}'
agent-browser --session docs-case-router wait '[data-testid="lazy-content"]'
lazy_panel_id="$(npx @genie-react/cli call react_find_components \
  '{"query":"LazyPanel","exact":true}' --json | \
  jq -er 'if (.matches | length) == 1 then .matches[0].id else error("expected one LazyPanel") end')"
npx @genie-react/cli call react_toggle_suspense_fallback \
  "$(jq -cn --argjson id "$lazy_panel_id" '{id:$id,showFallback:true}')" --json
suspense_state="$(npx @genie-react/cli call react_error_state '{}' --json)"
printf '%s\n' "$suspense_state" | \
  jq -e 'any(.suspended[]; .forced == true and .isFallbackShowing == true)'
agent-browser --session docs-case-router get text body | rg -F 'Loading lazy panel…'

Error boundary

Navigate to the error fixture and discover its current child ID before forcing the boundary:

export GENIE_BRIDGE_URL=ws://localhost:3030/__genie/ws
export GENIE_SESSION=docs-case-router
npx @genie-react/cli call react_reset_overrides '{}'
npx @genie-react/cli call router_navigate '{"to":"/error"}'
npx @genie-react/cli call devtools_wait \
  '{"condition":"navigation","name":"/error","timeoutMs":5000}'
agent-browser --session docs-case-router wait '[data-testid="throw"]'
bomb_id="$(npx @genie-react/cli call react_find_components \
  '{"query":"Bomb","exact":true}' --json | \
  jq -er 'if (.matches | length) == 1 then .matches[0].id else error("expected one Bomb") end')"
npx @genie-react/cli call react_force_error_boundary \
  "$(jq -cn --argjson id "$bomb_id" '{id:$id,forceError:true}')" --json
error_state="$(npx @genie-react/cli call react_error_state '{}' --json)"
printf '%s\n' "$error_state" | \
  jq -e 'any(.caughtErrors[]; .forced == true and .boundaryName == "Boundary")'
agent-browser --session docs-case-router get text body | \
  rg -F 'Caught: Simulated error coming from DevTools'

forced:true separates these development checks from organic errors and suspensions.

Verify the UI

Use the browser driver to check:

  • The skeleton matches the final content size.
  • Buttons and links keep their position.
  • The fallback has a useful accessible name or status.
  • The error stays inside the intended boundary.
  • Retry returns to real content.

Restore

Restore the Query session and verify the synthetic marker is gone:

export GENIE_BRIDGE_URL=ws://localhost:3000/__genie/ws
export GENIE_SESSION=docs-case-start
npx @genie-react/cli call query_restore_state \
  '{"queryKey":["dashboard","metrics"]}' --json
query_state="$(npx @genie-react/cli call query_get \
  '{"queryKey":["dashboard","metrics"]}' --json)"
printf '%s\n' "$query_state" | jq -e 'has("simulatedState") | not'
agent-browser --session docs-case-start get text body | rg -F 'Acme NA #1'

Reset the Router session and verify the real child is visible again:

export GENIE_BRIDGE_URL=ws://localhost:3030/__genie/ws
export GENIE_SESSION=docs-case-router
reset="$(npx @genie-react/cli call react_reset_overrides '{}' --json)"
printf '%s\n' "$reset" | jq -e '.remaining == 0'
agent-browser --session docs-case-router get text body | rg -F 'Throw'
unset query_state suspense_state error_state reset lazy_panel_id bomb_id

Finish only after the target UI checks pass and each app returns to its real state. Record the state you forced, the visible result, and the cleanup check. The cleanup reference covers interrupted sessions.

On this page