How Does React Handle Version Support?
React does not publish fixed end-of-life dates. Instead of deprecating old major versions on a set schedule, the React team commits to backporting security fixes to every major version that is affected by a given vulnerability -- regardless of how old that release is. There is no "security-only" phase and no hard cutoff after which a major version is abandoned.
This model reflects the sheer scale of the ecosystem. With millions of React applications in production, the team treats version compatibility as an ongoing obligation rather than a ticking clock. In practice, most teams upgrade not because a version is "expired" but because new features, improved performance, or shifting dependencies make upgrading worthwhile.
| Attribute | React |
|---|---|
| EOL model | No fixed EOL dates -- support tied to active major versions |
| Security fix policy | Backported to all affected major versions |
| Release channels | Latest (stable), Canary (pre-release), Experimental |
| Major release cadence | Infrequent -- years between major versions |
| Minor release cadence | Frequent -- most new features ship as minors |
The stable "Latest" channel follows semver strictly: patch releases address critical bugs and security vulnerabilities; minor releases add new features or non-critical fixes; major releases introduce breaking changes. Because breaking changes are rare by design, minor releases are often where the interesting things happen.
Two additional channels exist for framework authors and early adopters. The Canary channel tracks the main branch and is used as release candidates; frameworks like Next.js pin to specific Canary builds to ship new React capabilities on their own schedule. The Experimental channel includes unstable APIs that may never reach stable -- it is not intended for production use.
Reference: react.dev/community/versioning-policy
What Breaks When You Fall Behind on React Versions?
The risks of running an older React version are primarily about the surrounding ecosystem, not React itself. React's backward compatibility commitment means old code rarely stops working in the version you installed it under -- the problem is that everything around React keeps moving.
Dependency lock-in
Libraries like React Router, Redux Toolkit, React Query, and component libraries track the React release table above. When you can't upgrade React, you fall behind on these libraries too. Eventually a library you depend on drops support for your React version, and you are stuck choosing between skipping the upgrade or doing two big upgrades simultaneously.
Missing compiler and runtime optimizations
Each major React version brings significant internal changes -- concurrent features, automatic JSX transform, the React Compiler introduced to optimize re-renders. Teams on older versions carry manual optimization overhead (memoization, careful shouldComponentUpdate logic) that newer versions handle automatically. Over time, the gap in render performance and developer ergonomics compounds.
Security exposure on transitive dependencies
React itself has a strong security track record, but a frozen React version often means frozen versions of supporting packages -- babel transforms, bundler plugins, dev tools. These transitive dependencies are where CVEs tend to appear. Staying current on React makes it much easier to stay current on the full dependency tree.
New browser APIs and server environments
React Server Components, streaming SSR, and transitions require infrastructure that older React versions cannot provide. If your team wants to adopt modern rendering patterns or use frameworks like Next.js at their full capability, an outdated React version becomes a hard blocker -- not just a soft risk.
What Actually Changes After a Major React Version Matures?
React does not cut off older major versions the way runtimes like Node.js do. As shown in the release table above, the previous major version continues to receive security backports if a vulnerability is found. What does change is the center of gravity of the ecosystem.
Once a new major is stable and widely adopted, documentation, community support, and third-party tooling shift toward it. Stack Overflow answers reference new APIs. Library maintainers start requiring the current stable version in their peer dependencies. Official codemods and upgrade scripts are written for the transition, making the upgrade path much smoother than it would be if you waited another two major versions.
The React team has historically provided automated migration tools (codemods) for breaking changes -- the codemod for the hooks migration and the JSX transform change are good examples. These tools are published at release time, so upgrading immediately after a major release is often easier than upgrading a year later when your codebase has diverged further.
In practice, the realistic risk of running an older React major is not a sudden deprecation notice. It is a slow accumulation of incompatibilities and missed capabilities that eventually makes the upgrade cost higher than it would have been if addressed earlier.
How To Check Your React Version
The quickest way is to check package.json in your project root:
cat package.json | grep '"react"'
This shows the version range you have declared. To see the exact installed version (which may differ from the declared range if your lockfile is stale):
npm list react
Or with yarn:
yarn list --pattern react
Or with pnpm:
pnpm list react
To check the version at runtime inside your application (useful for debugging SSR environments or unexpected version mismatches):
import React from 'react';
console.log(React.version);
If you want to see what the latest stable release is without leaving your terminal:
npm view react version
A common gotcha: projects using monorepos or workspaces sometimes have multiple React versions installed in different packages. Running npm list react --all (or the equivalent for your package manager) will surface these duplicates, which can cause subtle bugs and the "two copies of React" error in hooks.
FAQ
Q1: Does React follow an LTS model like Node.js?
No. React does not designate specific versions as long-term support releases. The support model is simpler: security fixes are backported to all affected major versions as needed, with no formal distinction between "active" and "maintenance" tiers. If you need predictable upgrade windows, that responsibility sits with your organization, not React's release policy.
Q2: Is it safe to use the Canary channel in production?
Only if you follow the pinned Canary workflow. Frameworks like Next.js ship specific pinned Canary builds that are stable in practice -- they test against those builds continuously. Using an unpinned canary tag in your own production app is risky because Canary releases can include breaking changes between successive builds. The rule is: pin a specific Canary version, test thoroughly, or stay on Latest.
Q3: What is the difference between a patch release and a minor release in React?
React reserves patch releases for critical bugs and security vulnerabilities only -- things that can be adopted with zero expected side effects. Minor releases are used for everything else: new features, internal refactors, performance improvements, and non-critical bug fixes. The bar for a patch release is intentionally very high. If a change carries any meaningful risk of unexpected behavior, it ships as a minor.
Q4: How do I know when a React API is going to be removed?
React's policy is to warn before breaking. Deprecation warnings are added in development builds before the API is removed in a major release. If you run your app in development mode with no console warnings on the current stable version, it is expected to be compatible with the next major release. This is a deliberate part of the upgrade path -- the compiler tells you what to fix before the removal happens.
Q5: Can I mix different React versions across packages in a monorepo?
Technically yes, but it causes problems in practice. React relies on a single shared instance for hooks and context to work correctly. Two packages rendering into the same tree with different React versions will produce the "invalid hook call" error, context will not propagate across the version boundary, and DevTools will behave inconsistently. The standard solution is to hoist React to a root-level dependency and enforce a single version across the entire workspace.
