All jobs

Add Optimistic UI to a React Router Form from a Linear Issue

Yes. An agent picks up the Linear issue, makes form submission update the list immediately with useFetcher, rolls back on failure and opens a pull request.

Run this on RushIn a Project that has your repository.

What the agent does

The run starts from the repository URL in resources: the agent clones it into its own cloud sandbox and opens the default framework template. In remix-run/react-router-templates, that app lives under default/. Its package.json uses React 19, react-router with @react-router/node and @react-router/serve, Vite 8, and scripts typecheck (react-router typegen && tsc), build and dev. There is no test script in the template itself; Playwright harnesses sit in the repo's .tests/ folder.

The default UI has no form yet. app/routes/home.tsx only exports meta and renders <Welcome />. app/welcome/welcome.tsx is a static landing page with documentation links. app/root.tsx sets up Layout, Links and an ErrorBoundary, with no loader or action. The issue asks to make the default template's form submission update a list immediately with useFetcher and roll back with an error message if the action fails, so the agent has to add the list, the action and the optimistic UI in one pass.

The agent adds a small in-memory or file-backed list on the home route: a loader that returns items, an action that creates one and can fail on invalid input, and a useFetcher form that prepends the pending item to the list on submit. On a failed action it removes the optimistic row and shows the error message from the action data. Styling can reuse the existing Tailwind classes from the welcome page so the change stays in app/routes/ and related components.

Proving it means typecheck, build and new tests. The agent adds a Vitest or Testing Library test that mocks the fetcher path or exercises the optimistic state helpers, runs pnpm typecheck and pnpm build from default/, and runs the new tests. The monorepo's Playwright config under .tests/ can stay unused unless the agent adds a focused browser case.

Start it with one API call

POST https://api.prix.dev/o/{org}/sessions with this body:

{
  "agent": "claude",
  "input": "A Linear issue labeled agent says: \"Add Optimistic UI to a React Router Form. Make the default template's form submission update the list immediately with useFetcher and roll back with an error message if the action fails.\" Do the work in this repository, add tests for it, run pnpm typecheck, pnpm build and the new tests, and open a pull request that links back to the issue.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
  1. Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
  2. Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
  3. Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

What comes back

What comes back is a pull request. Its diff adds a loader, action and useFetcher form on the default template's home route, plus tests, and may trim the static Welcome block to make room for the list. The description quotes the issue and links back to it.

The checks on the pull request run pnpm typecheck, pnpm build and the new tests inside default/. A reviewer reads the optimistic prepend, the rollback on action error, and confirms a successful action still matches what the loader returns after revalidation. Other templates such as node-custom-server stay untouched.

The session read keeps the record behind the pull request: typegen, tsc, the production build and the test output. Anyone who questions a flicker on rollback can see the exact assertion the agent ran.

Run it on every Linear issue labelled agent

The Project's automation turns the label into the run. In the console, add an automation with a Linear trigger on linear.issue.labeled filtered to the agent label and this prompt; each labelled UI issue starts its own run in its own sandbox and opens its own pull request on the templates repository.

If the automation is not wanted, a small server can do the same: receive Linear's issue webhook, put the issue text into input, and POST to /o/{org}/sessions with the react-router-templates repository in resources. A run.completed webhook on the API key posts the pull request when typecheck and build finish.

The use case this job follows, with its automation and trigger: Turn a Linear label into a PR.

Why run it on Rush?

On a laptop this job needs pnpm for the workspace lockfile, a Node version Vite 8 accepts, and a full react-router typegen before tsc will see route types. Each run on Rush boots its own sandbox with that already in reach, runs typecheck, build and tests itself, and keeps every command and its output.

Optimistic UI work is awkward to review from screenshots alone. A labelled issue becomes a sandbox run whose trajectory shows the failing action fixture and the rollback assertion, so reviewers see behaviour, not only the diff.

Frequently asked questions

The default template has no form. Where does the list go?

On the home route under default/app/routes/. The agent adds the list and form there (or a small component it imports) so the Welcome-only page becomes an interactive example without starting a second template package.

Why useFetcher instead of Form with useNavigation?

The issue names useFetcher so the submission can update the list without navigating away. The agent follows that API and reads fetcher.data for the error message on failure.

Does the agent change unstable RSC templates too?

No. Only default/ is in scope. unstable_rsc-framework-mode and node-custom-server remain as they are.

Is Playwright required?

Not unless the agent chooses it. The prompt asks for new tests plus pnpm typecheck and pnpm build; a unit or component test that covers optimistic prepend and rollback is enough.