facebook/reactMITd083ec1Report / request removal

Legacy APIs & Migration Warnings

This page covers the compatibility surface React retains for older applications: legacy roots, legacy context, DOM escape hatches, legacy server rendering, and StrictMode warning collection.

It exists because these APIs do not all have the same behavior or availability: legacy entry points create LegacyRoot roots, while render and unmountComponentAtNode are rejected when disableLegacyMode is enabled.

Sources: packages/react-reconciler/src/ReactRootTags.js:12, packages/react-reconciler/src/ReactRootTags.js:13, packages/react-dom/src/client/ReactDOMRootFB.js:388-434, packages/react-dom/src/client/ReactDOMRootFB.js:393-400, packages/react-dom/src/client/ReactDOMRootFB.js:436-444

Core concepts

Root modes

A root mode identifies whether a reconciler root is legacy or concurrent: LegacyRoot is 0, while ConcurrentRoot is 1.

The shown legacy DOM path explicitly creates roots with LegacyRoot; the supplied createRoot excerpt only shows delegation to createRootImpl, so it does not establish which root tag that implementation selects.

Sources: packages/react-reconciler/src/ReactRootTags.js:12, packages/react-reconciler/src/ReactRootTags.js:13, packages/react-dom/src/client/ReactDOMRootFB.js:275-287, packages/react-dom/src/client/ReactDOMRootFB.js:131-146

Legacy DOM APIs

Legacy DOM APIs are the older entry points that render into a container, remove a legacy root, or locate a host node. render delegates to legacyRenderSubtreeIntoContainer, unmountComponentAtNode removes a stored legacy root, and findDOMNode resolves an element or host instance.

Sources: packages/react-dom/src/client/ReactDOMRootFB.js:388-434, packages/react-dom/src/client/ReactDOMRootFB.js:436-509, packages/react-dom/src/client/ReactDOMRootFB.js:356-386

Legacy context

Legacy context passes values through contextTypes and childContextTypes, using masked objects, merged child context, and stack cursors rather than the modern context API.

Sources: packages/react-reconciler/src/ReactFiberLegacyContext.js:73-110, packages/react-reconciler/src/ReactFiberLegacyContext.js:213-237

StrictMode warning collection

StrictMode warning collection exposes operations for recording unsafe-lifecycle and legacy-context warnings, flushing each category, or discarding pending warnings.

Sources: packages/react-reconciler/src/ReactStrictModeWarnings.js:19-25

How a legacy root is scheduled

The legacy path begins with render, validates the container, warns when the container belongs to a modern root, and then calls legacyRenderSubtreeIntoContainer. That helper either creates an initial legacy root or updates the existing _reactRootContainer.

SurfaceEntry pointBehavior shown by the source
Legacy renderrenderRejects when disableLegacyMode is enabled; otherwise warns that the app behaves as if running React 17.
Legacy unmountunmountComponentAtNodeRejects when disableLegacyMode is enabled and otherwise synchronously clears the stored legacy root.
Modern rootcreateRootDelegates to createRootImpl; the supplied excerpt does not show the selected root tag.
Modern hydrationhydrateRootDelegates to hydrateRootImpl; the supplied excerpt does not show the selected root tag.

A legacy initial mount clears existing container content, creates a LegacyRoot, installs the root marker, attaches supported events, updates synchronously, and flushes synchronous work.

const root = createContainer(
  container,
  LegacyRoot,
  null, // hydrationCallbacks
  false, // isStrictMode
  false, // concurrentUpdatesByDefaultOverride,
  '', // identifierPrefix
  wwwOnUncaughtError,
  wwwOnCaughtError,
  noopOnRecoverableError,
  noopOnDefaultTransitionIndicator,
  null, // transitionCallbacks
);

This block shows the legacy root tag and the synchronous initial-update path.

The legacy APIs are exposed only when disableLegacyMode is false; when it is true, render and unmountComponentAtNode log removal messages and throw an unsupported-API error. Calls against a container previously passed to createRoot are also diagnosed as unsupported in development.

The visible call path for an initial legacy render is:

Legacy root path — How does a legacy render reach the reconciler root?

Evidence

Sources: packages/react-dom/src/client/ReactDOMRootFB.js:388-434, packages/react-dom/src/client/ReactDOMRootFB.js:318-354, packages/react-dom/src/client/ReactDOMRootFB.js:393-407, packages/react-dom/src/client/ReactDOMRootFB.js:436-480, packages/react-dom/src/client/ReactDOMRootFB.js:131-146, packages/react-dom/src/client/ReactDOMRootFB.js:148-165, packages/react-dom/src/client/ReactDOMRootFB.js:263-302, packages/react-dom/src/client/ReactDOMRootFB.js:275-287, packages/react-dom/src/client/ReactDOMRootFB.js:393-400, packages/react-dom/src/client/ReactDOMRootFB.js:436-444, packages/react-dom/src/client/ReactDOMRootFB.js:414-423, packages/react-dom/src/client/ReactDOMRootFB.js:449-458

How legacy context moves through a render

A class is recognized as a legacy context provider when it defines childContextTypes, unless disableLegacyContext is enabled. At the top level, pushTopLevelContextObject pushes the parent context and a “did perform work” flag onto separate cursors. A provider pushes its memoized merged child context and remembers the previous context.

A consumer receives only the keys declared in contextTypes. getMaskedContext copies those keys from the unmasked context and caches both forms on the class instance so React can avoid recreating the masked object when the source context is unchanged.

A provider calls getChildContext, verifies that every returned key appears in childContextTypes, and merges the result with the parent context. Missing getChildContext produces a development warning and returns the parent context instead.

When the provider’s context changes, invalidateContextProvider recomputes the merged object, pops the old stack entries, and pushes the new context and change flag. Consumers use the change cursor through hasContextChanged. The supplied excerpts show separate stack-unwind functions, popContext and popTopLevelContextObject, but do not show their callers.

This is the migration cost: the reconciler must inspect declared context keys, cache masked and unmasked objects, merge provider output, track whether work occurred, and preserve stack state across the render path.

Sources: packages/react-reconciler/src/ReactFiberLegacyContext.js:120-127, packages/react-reconciler/src/ReactFiberLegacyContext.js:147-164, packages/react-reconciler/src/ReactFiberLegacyContext.js:213-237, packages/react-reconciler/src/ReactFiberLegacyContext.js:73-110, packages/react-reconciler/src/ReactFiberLegacyContext.js:167-210, packages/react-reconciler/src/ReactFiberLegacyContext.js:239-277, packages/react-reconciler/src/ReactFiberLegacyContext.js:112-118, packages/react-reconciler/src/ReactFiberLegacyContext.js:129-136, packages/react-reconciler/src/ReactFiberLegacyContext.js:138-145

How warnings steer migration

findDOMNode returns a supplied DOM element directly. In development, it warns when called during render because the operation can depend on stale data from the previous render; otherwise it uses findHostInstanceWithWarning in development and findHostInstance outside development.

When legacy context support, controlled by disableLegacyContext, is disabled, class components using childContextTypes or contextTypes receive messages directing them to React.createContext(). The Fizz class-component path also contains a disableLegacyContext branch that warns for those legacy declarations and recommends React.createContext().

ReactStrictModeWarnings exposes recordUnsafeLifecycleWarnings, flushPendingUnsafeLifecycleWarnings, recordLegacyContextWarning, flushLegacyContextWarning, and discardPendingWarnings. The supplied class-component excerpt shows a warning condition involving getDerivedStateFromProps or getSnapshotBeforeUpdate, but it does not enumerate every unsafe lifecycle or show the callers that flush the pending warning collections.

findStrictRoot walks from a fiber toward its ancestors and retains the last ancestor encountered with StrictLegacyMode. setToSortedString converts a warning-name set into a sorted, comma-separated string, which supports stable warning text.

Sources: packages/react-dom/src/client/ReactDOMRootFB.js:359-385, packages/react-reconciler/src/ReactFiberClassComponent.js:349-367, packages/react-server/src/ReactFizzClassComponent.js:365-383, packages/react-reconciler/src/ReactStrictModeWarnings.js:19-25, packages/react-reconciler/src/ReactFiberClassComponent.js:630-636, packages/react-reconciler/src/ReactStrictModeWarnings.js:28-40, packages/react-reconciler/src/ReactStrictModeWarnings.js:42-48

Legacy server rendering

The legacy server implementation builds a Fizz request, starts work, aborts still-pending suspended work before writing, and starts flowing into a string destination. The destination appends each non-null chunk to result, while destroy records a fatal error. Fatal errors are thrown, and successful rendering returns the accumulated string.

Sources: packages/react-dom/src/server/ReactDOMLegacyServerImpl.js:63-85, packages/react-dom/src/server/ReactDOMLegacyServerImpl.js:41-57, packages/react-dom/src/server/ReactDOMLegacyServerImpl.js:86-101

How it connects

The DOM entry points hand legacy roots to the reconciler, so root structure belongs with Fiber Architecture and scheduling details with Work Loop & Scheduling

Migration from contextTypes and childContextTypes should be understood alongside Context API

The modern DOM root entry points connect to ReactDOM Host Config while legacy server output connects to Server Rendering (Fizz)

Sources: packages/react-dom/src/client/ReactDOMRootFB.js:275-302, packages/react-reconciler/src/ReactFiberClassComponent.js:349-367, packages/react-dom/src/client/ReactDOMRootFB.js:131-146, packages/react-dom/src/client/ReactDOMRootFB.js:148-165

Key takeaways

  • LegacyRoot is explicitly used by the legacy DOM creation path, whose initial update is synchronous.
  • render and unmountComponentAtNode are rejected when disableLegacyMode is enabled.
  • Legacy context filters, caches, merges, and tracks values through stack cursors.
  • findDOMNode, legacy context declarations, and unsafe-lifecycle warnings are migration surfaces.
  • The supplied warning excerpts expose batching operations but do not enumerate every unsafe lifecycle or flush call site.

Want this for your repos?

Try Angada AI Wiki