How I used Cursor to Migrate Frameworks

February 20th, 2026 — 12 min read

Assorted-color item lot
Assorted-color item lot
No translations available.Add translation

Yesterday I decided it was time to upgrade the dependencies on kentcdodds.com (the site you're probably reading this on). I've actually been working on my site a bit recently to add a few new features (have you tried the new semantic search or keyboard shortcuts yet try "How did Kent get his first job?" and hit ? to see the keyboard shortcuts).

Just for context, kentcdodds.com is a pretty big project (currently sitting at over 42k lines of code with over 330k words of content). For a quick look at the scope of this thing, check out introducing the new kentcdodds.com for a high-level overview.

I gave Cursor (Composer 1.5) the following prompt:

Make me a list of packages in this project that are not on the latest version, grouped by difficulty of upgrading.

It gave me this table:

Here’s the list of outdated packages, grouped by upgrade difficulty:


Easy (patch/minor within same major)

Usually safe to upgrade with npm update or a small version bump.

PackageCurrentLatest
@conform-to/react1.16.01.17.1
@conform-to/zod1.16.01.17.1
@epic-web/config1.21.31.24.0
@modelcontextprotocol/sdk1.25.31.26.0
@playwright/test1.58.11.58.2
@prisma/adapter-better-sqlite37.3.07.4.0
@prisma/client7.3.07.4.0
@tailwindcss/vite4.1.184.2.0
@types/react19.2.1019.2.14
esbuild0.27.20.27.3
framer-motion12.29.212.34.2
isbot5.1.345.1.35
metascraper5.49.215.49.24
metascraper-description5.49.215.49.24
metascraper-image5.49.215.49.24
metascraper-title5.49.215.49.24
msw2.12.72.12.10
prisma7.3.07.4.0
tailwindcss4.1.184.2.0
vite-plugin-cjs-interop2.4.02.4.1

oauth/ subpackage:

PackageCurrentLatest
@epic-web/config1.21.01.24.0
prettier3.5.33.8.1
wrangler4.19.14.67.0
zod3.25.573.25.76 (within v3)

Medium (single major version bump)

One major version upgrade; check migration guides and changelogs.

PackageCurrentLatest
@faker-js/faker8.4.110.3.0
@octokit/plugin-throttling9.6.111.0.3
@octokit/rest20.1.222.0.1
@sentry/profiling-node9.47.110.39.0
@sentry/remix9.47.110.39.0
@sentry/vite-plugin3.6.14.9.1
@sindresorhus/slugify2.2.13.0.0
@testing-library/react15.0.716.3.2
@types/uuid9.0.810.0.0
@vitejs/plugin-react4.7.05.1.4
bcrypt5.1.16.0.0
chokidar3.6.05.0.0
close-with-grace1.3.02.4.0
concurrently8.2.29.2.1
cookie0.7.21.1.1
cross-env7.0.310.1.0
date-fns3.6.04.1.0
dotenv16.6.117.3.1
eslint9.39.210.0.0
express4.22.15.2.1
glob10.5.013.0.5
helmet7.2.08.1.0
jest-environment-jsdom29.7.030.2.0
jest-watch-typeahead2.2.23.0.1
lru-cache10.4.311.2.6
match-sorter6.4.08.2.0
p-limit5.0.07.3.0
p-queue8.1.19.1.0
path-to-regexp6.3.08.3.0
react-error-boundary4.1.26.1.1
react-lite-youtube-embed2.4.03.5.0
rimraf5.0.106.1.3
uuid9.0.113.0.0
vite-env-only2.4.13.0.3
vite-tsconfig-paths4.3.26.1.1

oauth/ subpackage:

PackageCurrentLatest
@cloudflare/workers-oauth-provider0.0.50.2.3
eslint9.28.010.0.0

Hard (multiple majors or framework-critical)

Multiple major versions or core framework pieces; may need patches.

PackageCurrentLatest
@remix-run/dev2.9.22.17.4
@remix-run/express2.9.22.17.4
@remix-run/node2.9.22.17.4
@remix-run/react2.9.22.17.4
@remix-run/router1.16.11.23.2
@remix-run/serve2.9.22.17.4
@remix-run/server-runtime2.9.22.17.4
@types/node24.10.925.3.0
@xstate/react3.2.26.0.0
vitest1.6.14.0.18
vite5.4.217.3.1
xstate4.38.35.28.0
zod3.25.764.3.6

Remix: There are patches in other/patches/ for @remix-run/react, @remix-run/router, and react-router-dom. Upgrading Remix will likely require re-checking or re-applying these patches.

Vite / Vitest: Vite 5 → 7 and Vitest 1 → 4 are large jumps. Expect config and plugin changes.

XState: xstate 4 → 5 and @xstate/react 3 → 6 involve major API changes.

Zod: 3 → 4 is a breaking change and affects many consumers (e.g. Conform, Remix).


Summary: 19 easy, 42 medium, 14 hard. Start with the easy group; for Remix, review the patches before upgrading.

A week or two ago I had Cursor update Tailwind from v3 to v4 (otherwise that would've showed up here too).

So next thing I did was fork the conversation and told it to update all the easy ones. That went without a hitch.

I then had it do the medium ones. This one did end up with a couple issues, but wasn't that bad. It handled most the breaking changes gracefully.

This only really works because I have some pretty good tests and documentation that I made when I was actively developing the project (with my bare hands, like some kind of caveman).

Major changes

I decided to do the major changes one at a time. I had it run the vite (v5 -> v7) and vitest (v1 -> v4) upgrades together. That went without a hitch.

Feeling pretty good about this, I had it run the zod upgrade. I've done the zod v4 upgrade before (with the Epic Stack and other projects based on it) so I knew that conform had a special export for zod v4, so I said:

Let's do zod now. Conform has a special export for zod 4 integration. Use that.

Everything was smooth.

Then I decided to make a new conversation and told it to upgrade xstate. I use xstate for the Call Kent Podcast in-browser recorder (my agent says there's about 200 lines of xstate related code in this project). So it's not a huge dep, but I remember when I originally wrote this and it was challenging.

I created a new conversation because it had told me that the @xstate/react v3 -> v6 upgrade was a major upgrade and I wanted to be sure to handle it carefully.

Please update xstate and @xstate/react. Before doing this, look up migration docs and follow those.

It searched the web and found migration docs, planned the migration, and executed the plan to perfection.

Double-Major changes

Ok, so first I have to say that migrating from Remix v2 to React Router v7 is not a true framework migration, but rather a dependency upgrade because Remix v2 was just a wrapper around React Router. In v7, the Remix team moved everything to React Router. They did the responsible thing by making "future flags" so you could incrementally adopt new features to avoid a big bang upgrade.

I had enabled none of those future flags.

I knew this one would be a pretty big upgrade, so I decided to use Cursor's new long-running background agent feature.

Here's the prompt I used:

Upgrade us from Remix v2 to React Router v7. Before doing this, look up relevant migration docs/upgrade guides. We have some remix-specific dependencies as well. Make sure their latest versions work with React Router. If they don't, find alternatives.

We have some patch files. These are to make it so we can add headers to remix fetchers and things. I don't think that feature will ever exist so I want you to delete those patch files and instead find an alternative solution (a simple fetch with a revalidation call should be sufficient, but double check my thinking on that).

It created a plan and I gave it a couple bits of feedback/answered some questions:

  • I want to use react-router-auto-routes, look up the docs for that. This will require moving files around a lot
  • REMIX_DEV_HTTP_ORIGIN is completely unnecessary in RR

Manual fetch, the reason we do this is to skip the unnecessary buffering of the request body in a server that can't do anything with this data anyway

This was an interesting decision because before I was using remix-flat-routes for filesystem routing. react-router-auto-routes is what the Epic Stack uses now (check the decision doc). Normally when upgrading a framework, you probably want to change as little as possible. But I figured the agent would figure it out. Living dangerously 😎

Anyway, that was it. Then I sent it off to do its thing and it did an awesome job. However, it did struggle with the routing convention change (predictable 😆).

So I sent this:

you need to fix the way our file structure is so it makes routing work. Right now with the way we have things in this branch, here's what I get if I run npx react-router routes: ... routes output ... If I checkout main and run npx remix routes I get: ... routes output ... You need to fixup our directory structure to make react-router-auto-routes match our original routing structure as closely as possible. The order can be different, and the file paths will definitely be different, but the basic structure must be almost identical. Lookup the react-router-auto-routes (what we want and have in this branch) docs and compare it to the remix-flat-routes docs (what we have currently in main).

The next time I tried it out, the routing worked perfectly (give the agent a way to check its work and it performs much better!).

Then I noticed that the agent had tried to keep changes minimal and I decided we could be more amitious:

Could you adopt the latest on how to handle types in Loaders, actions, components, etc based on the react router documentation? You can make a plan but don't wait for me to approve it. Just go for it. I'm going to sleep and want this all to be done when I wake. You got this!

Then I went to sleep. The agent kept running in the background for 21 minutes and finished that.

While I slept, it iterated a few times with Cursor BugBot. I have auto-fix enabled, so when BugBot finds issues, it automatically fires off an agent to fix the bug. It had managed things really well!

This morning, I pulled things down and played with stuff. It worked really well. Both BugBot and CodeRabbit had identified issues, so I kept the agents going to fix things as they were found. After a few more iterations, I merged it into main.

Kent C. Dodds ⚡ avatar
Kent C. Dodds ⚡ @kentcdodds
Amazing that merging PRs like this (from cursor cloud agents) is starting to feel routine...
Tweet media

Just amazing!

Thoughts

Life has gotten a little hectic lately:

Kent C. Dodds ⚡ avatar
Kent C. Dodds ⚡ @kentcdodds
I don't know about you, but recently my days have been extremely chaotic. I'm often working on several projects at once with sometimes a couple cloud agents running on each while guiding an agent more directly locally. Also handling email, 𝕏, and DMs between it all. It's not

My laptop is constantly pinging me with notifications from Cursor local and email notifications from Cursor Cloud Agents. I'm normally an inbox 0 and close the browser tab when I'm done with it kind of person, but it's honestly hard to keep track of everything. I have emails coming in from github PRs and I have so many tabs open for cloud agents and github PRs. Not to mention the 5+ cursor editors I have open locally for different projects. I'm all over the place. It's crazy. I love it 😂

Someone asked me how many prompts I send a day which I thought was a funny question because I don't think about it. But I'm pretty sure I send hundreds of prompts daily (depends on what you count as a prompt, but I'm including follow-up messages in conversations). I don't know, I wouldn't be terribly surprised to find out I'd sent 1000 prompts. I mean, just in this site, just this morning I have 25 agents that I triggered myself. Some are bigger, some are smaller. And I've got other projects going. Things are happening!

I've never had so much fun building stuff. At the same time, we're walking dangerous territory:

Kent C. Dodds ⚡ avatar
Kent C. Dodds ⚡ @kentcdodds
@johnlindquist YES This has become a problem for me. It's hard to take lunch breaks. It's hard to stop at the end of the day. It's hard to not pick up my phone during family time to just "send off a quick follow-up"
3 7

And

Kent C. Dodds ⚡ avatar
Kent C. Dodds ⚡ @kentcdodds
I am now always "in the middle of something" and it's very difficult to force myself to stop to do anything but send another prompt.

Finding balance is hard when the next big problem to be solved is just one more prompt away.

Hope you enjoyed reading this! And seriously, try out the new semantic search feature of the site! It was fun to build and I plan on teaching how to build something like this in the future (among other things), so sign up for my mailing list!

Cheers!

The EpicAI.pro logo

Epic AI

Learn to build AI-powered applications.

Visit course
Kent C. Dodds
Written by Kent C. Dodds

Kent C. Dodds is a JavaScript software engineer and teacher. Kent's taught hundreds of thousands of people how to make the world a better place with quality software development tools and practices. He lives with his wife and four kids in Utah.

Learn more about Kent

Have a question about this article?

Bring it to the Call Kent podcast. Ask on /calls and I may answer it on the podcast.

Place a call

If you found this article helpful.

You will love these ones as well.