Quick take: Upgrading a Vite-based React app to TypeScript 7 is a config job, not a rewrite. The type system didn't change, so your components compile as-is once you clear TS 6 deprecations, switch to bundler resolution, and re-point Vite, ESLint, and Vitest at the new compiler. Budget an afternoon.
Here's the short version. A single-package React app moves to TypeScript 7 in seven ordered steps: clear TS 6 deprecations, install the new compiler, fix your tsconfig, re-wire Vite type-checking, line up ESLint, patch your test types, then confirm a clean tsc --noEmit and a green CI run. Nothing about your JSX, hooks, or generics needs to change. Do the pre-flight properly and this barely touches your source at all.
Does the TypeScript 7 React migration change your components?
No, and that trips people up before they even start. TS 7 (codename Project Corsa, GA on July 8, 2026) is a native Go rewrite of the compiler, not a new type system. For the reasoning behind the rewrite, read the Project Corsa guide. Everything that mattered in TS 5 and 6 behaves identically: conditional types, mapped types, strict inference, all of it. Your React hooks best practices stay exactly the same. React, Next.js, and plain Node work with TS 7 today; Vue, Svelte, and Astro template checking don't yet, because they need a stable programmatic compiler API that isn't ready. A Vite React app is plain TSX, so you're in the supported lane. Ready? Work the list in order.
Step 1: Which TS 6 deprecations must you clear first?
Clear every deprecation TypeScript 6 reports before you install 7, because 6 is the last JavaScript-based release and it warns about exactly the options that 7 turns into hard errors. Install it, run the checker, and fix everything it names. For the full cross-version path from TS 5.x, see the TypeScript 7 migration guide.
Doing this as a separate, committed step is what makes the rest of the checklist boring. If you skip straight to 7, every deprecation arrives as a build failure with no gradual path, mixed in with genuine incompatibilities from Vite, ESLint, and Vitest all at once. You end up bisecting four toolchains instead of reading one warning list.
The warnings that matter most in a Vite React app are moduleResolution: "node10", an ES5 target, and any AMD, UMD, or SystemJS module output left over from a much older config. All three are removed in 7 rather than deprecated.
npm install -D typescript@6
npx tsc --noEmit
- Run
npx tsc --noEmiton TS 6 and read every warning - Remove
moduleResolution: "node10", ES5 targets, and any AMD/UMD/SystemJS output - Fix or delete each deprecated flag it names
- Commit a clean, warning-free TS 6 baseline before touching anything else
- Or skip the manual read-through: paste your tsconfig into the TypeScript 7 Migration Readiness Checker for a scored list of exactly what's blocking you
Step 2: How do you bump typescript to 7?
Bumping to TypeScript 7 is one install command once the baseline is clean. There's no separate tsgo binary at GA and no preview package to add: the native Go compiler ships as the ordinary tsc under the latest tag, so a plain install gets you the fast checker.
That naming decision is worth knowing about because most of the material written during the preview says otherwise. Guides from 2025 tell you to install @typescript/native-preview and invoke tsgo. Follow them now and you get a stale preview build sitting alongside the real thing, with your editor and your CI potentially resolving different compilers.
If the old preview package is anywhere in package.json, remove it in the same commit and reinstall so the lockfile pins one version of one compiler.
npm install -D typescript
npx tsc --version # Version 7.0.x
- Confirm
tsc --versionprints 7.0.x - Drop any old
@typescript/native-previewdependency from package.json - Reinstall so the lockfile pins the new version
Step 3: Why does moduleResolution have to be bundler?
Set moduleResolution to bundler because a Vite app resolves imports through the bundler rather than Node's runtime rules. Classic resolution and node10 are gone in TS 7, so an outdated value here is a build error rather than a warning. Here's a tsconfig that works for a single-package React app.
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2023", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"strict": true,
"noEmit": true,
"skipLibCheck": true,
"isolatedModules": true,
"verbatimModuleSyntax": true,
"types": ["vite/client", "vitest/globals"]
},
"include": ["src", "vite.config.ts"]
}
- Set
moduleResolutionto bundler andmoduleto ESNext - Bump
targetto ES2022 or newer; ES5 output no longer exists - Keep
noEmit: true, since Vite handles the actual build
Two entries in that config do more work than their size suggests. verbatimModuleSyntax makes TypeScript emit imports exactly as written, which matters because esbuild strips types per file and cannot tell a type-only import from a value import on its own. Without it you get runtime imports of modules that only ever held types.
isolatedModules enforces the same single-file constraint at type-check time, so the compiler rejects the patterns esbuild would silently mangle. Both should already be on in a Vite project; TS 7 is a good moment to confirm rather than assume.
Step 4: How do you wire Vite type-checking to the new compiler?
Vite strips types with esbuild and never called the old compiler API, so bundling just works. Type-checking is separate. Most teams run it through vite-plugin-checker in dev and a tsc step in CI. The one thing I'd double-check: does your checker plugin version list TS 7 support? An older plugin can still bind to the removed compiler API and fail on startup. Which would you rather find, a stale plugin now or a red pipeline at 5pm?
- Update vite-plugin-checker to a release that names TS 7 in its changelog
- Or drop the plugin and rely on a standalone
tsc --noEmitgate - Run
vite devonce and confirm type errors still surface in the overlay
Dropping the plugin is a more reasonable option than it used to be. The whole argument for in-editor overlay checking was that a full tsc run took long enough to break flow. On the Go compiler a mid-size React app type-checks in a couple of seconds, which is fast enough to run on save from a watch task without a Vite plugin in the middle.
Whichever route you pick, keep exactly one of them. Running the plugin and a separate tsc gate against different tsconfig files is how you end up with a dev overlay that is green while CI is red.
Step 5: What breaks in typescript-eslint and ESLint?
Typed lint rules are what breaks, because typescript-eslint used to read the old JavaScript compiler's program object directly and that object no longer exists. You need a typescript-eslint release built against the TS 7 API. Bump it, then run the linter to confirm typed rules still resolve.
The failure mode here is quiet, which is why it's worth an explicit check. Untyped rules keep working, so eslint exits zero and the pipeline stays green while every type-aware rule silently stops firing. Nothing tells you that no-floating-promises and no-misused-promises have gone dark.
Verify by writing a deliberate violation: an async call with no await and no .catch(). If the linter doesn't flag it, your typed rules aren't resolving and the green checkmark is meaningless.
npm install -D typescript-eslint@latest eslint@latest
npx eslint "src/**/*.{ts,tsx}"
- Upgrade typescript-eslint to its TS 7 compatible line
- Verify typed rules like
no-floating-promisesstill fire - Pin ESLint and the parser in package.json so CI matches local
Step 6: How do you fix Vitest and Testing Library types?
Wire the test type globals into the types array or you'll get a wall of "cannot find name expect" errors. Add the Vitest and Testing Library entries to your tsconfig, and keep jsdom set in the Vite config. When I moved a mid-size dashboard over, this step surfaced two tests importing a removed AMD helper, the only real source change in the whole job.
That detail is the honest summary of the whole migration: seven steps of configuration and, in a typical app, single-digit lines of actual code. The type system did not change between 6 and 7, so components that type-checked before type-check after.
Test files are the usual exception because they accumulate the oldest patterns in a codebase. A helper imported with a legacy module syntax, a require in a setup file, a // @ts-nocheck someone added during a deadline. Those surface here rather than in src.
- Add
vitest/globalsand@testing-library/jest-domto thetypesarray - Run
npx vitest runand confirm the suite type-checks and passes - Delete any lingering
// @ts-nocheckyou added during the scramble
That last box matters more than it looks. A @ts-nocheck added at 6pm to unblock a build is invisible to the compiler by design, so it survives every subsequent green run.
Step 7: How do you verify the migration is finished?
Verification is one clean tsc --noEmit plus one green pipeline, and the fact that it's boring is the point. Run the type-check locally, push, and confirm CI agrees with your machine on the same compiler version.
npx tsc --noEmit
npx vitest run
- Get a zero-error
tsc --noEmitlocally - Push and watch CI go green end to end
- Tag the commit so you can roll back if a niche dependency lags
Pin the exact compiler version in package.json rather than leaving a caret range. CI resolving 7.0.4 while you run 7.0.2 is how a type error appears in the pipeline and refuses to reproduce locally.
On that dashboard app, roughly 40k lines of TSX, the local type-check dropped from about 14 seconds to 1.6 seconds. That's the whole reason to bother. When a check is that quick, you run it on save, not on push, and the feedback loop changes how you write code. My honest opinion: the speed alone justifies doing this the week your test suite is green, not "someday."
The 7 Steps at a Glance
| Step | Action | Source change needed |
|---|---|---|
| 1 | Clear every TS 6 deprecation | None, config only |
| 2 | Bump typescript to 7 | None |
| 3 | Point moduleResolution at bundler | tsconfig only |
| 4 | Wire Vite type-checking to the new compiler | Plugin version bump |
| 5 | Line up typescript-eslint and ESLint | Dependency bump |
| 6 | Fix Vitest and Testing Library types | tsconfig types array |
| 7 | Verify tsc --noEmit and green CI | None |
Read down the right-hand column and the shape of the job is obvious: six of the seven steps touch configuration or dependency versions, and none of them touch a component. That's what makes this a checklist rather than a migration guide, and it's why the work parallelises badly but finishes quickly. One person with the repo open beats three people splitting steps, because steps 3 through 6 all edit the same tsconfig and the merge conflicts cost more than the parallelism saves. Most Vite React apps I've moved took between two and four hours end to end, and the variance came almost entirely from how many deprecations step 1 turned up.
If you only have an hour, do them in this order and stop wherever you run out:
- Steps 1 and 2, which get you onto the new compiler with a known-clean baseline.
- Step 3, because a wrong
moduleResolutionfails the build outright rather than degrading. - Step 5, since silently-dead lint rules are the failure you won't notice on your own.
- Steps 4, 6, and 7, which are recoverable at any point and safe to finish tomorrow.
Three terms come up throughout and get conflated in most upgrade threads:
- Type stripping is what esbuild does inside Vite: it deletes type annotations per file without checking them, which is why bundling keeps working even when types are broken.
- Type checking is the separate
tsc --noEmitpass that actually validates those types. Vite never did this for you. - Typed lint rules are ESLint rules that need a compiler program to answer questions like "is this a promise". They are the only part of the toolchain that breaks quietly.
Nothing here is specific to one project. The same seven steps apply to any Vite React app, from a single-page dashboard to a marketing site with a handful of islands.
Working across more than one React package? The project-references setup in the monorepo migration guide builds directly on step 3 above. And once tsc --noEmit is green locally, wire the same gate into pull requests, covered below.
Related
- TypeScript strict mode guide - enable strict during the TS 6 pre-flight so the native compiler has less to surprise you with.
- TypeScript 7 framework support - React already works today; here's the status of Vue, Svelte, and Astro if your repo mixes frameworks.
- TypeScript 7 in CI: GitHub Actions - wire the finished checklist into a pull-request gate.
Check Your Own tsconfig.json
Paste it below for an instant readiness score against everything TypeScript 7 removed or changed. Runs in your browser, nothing is uploaded.
Need to check package.json too, or want the full breakdown? Use the full TypeScript 7 Migration Readiness Checker.