findDOMNode Is Not a Function in React 19: Find It, Fix It

6 min read

TL;DR "findDOMNode is not a function" on React 19 means a dependency still calls ReactDOM.findDOMNode, which React 19 removed. Find the package in the stack trace (or grep node_modules), then replace it or patch it to read a ref. Pinning React 18 only works outside Next.js 15. Tested errors for seven popular old packages, with a replacement for each.

On this page
  1. Why React 19 throws "findDOMNode is not a function"
  2. Step 1: find the package from the stack trace
  3. Step 2: check the package against this table
  4. Step 3: fix it
  5. Replace the package
  6. Patch it to use a ref
  7. Pin React 18
  8. When the mask was for a card number
  9. FAQ
  10. Why do I get "findDOMNode is not a function" after upgrading to React 19?
  11. Can I add findDOMNode back to React 19?
  12. Does --legacy-peer-deps fix findDOMNode errors?
  13. Which packages break with "findDOMNode is not a function" on React 19?
  14. Is findDOMNode deprecated or removed?

findDOMNode is not a function on React 19 means one of your dependencies still calls ReactDOM.findDOMNode, and React 19 removed it. Your own code is rarely the cause. The fix is to find which package makes the call and replace it, or patch it to use a ref. Pinning React 18 also works, but Next.js 15 needs React 19, so that only buys time.

I make qodesign, animated React and Tailwind components for Next.js. While checking which old packages still work on React 19, we rendered each one with React 19.2.9 in a scratch project, and some again with React 18.3.1. The errors in the table below are the exact ones we got, and the fixes link to our guide for each package.

Why React 19 throws "findDOMNode is not a function"

findDOMNode took a class component instance and returned its DOM node. React deprecated it in October 2018 (v16.6.0), and the React 19 upgrade guide lists it under "Removed: ReactDOM.findDOMNode". The guide gives the reasons: it was slow, fragile to refactoring, only returned the first child and broke abstraction levels.

React 19's react-dom simply doesn't export it any more. A package that does ReactDOM.findDOMNode(this) reads undefined and calls it, so you get TypeError: findDOMNode is not a function. The prefix depends on how the package was bundled. react-input-mask gave us reactDom.findDOMNode is not a function, and bug reports for other packages show react_dom_1.default.findDOMNode or u(...).findDOMNode. They're all the same error.

npm doesn't always warn you first. react-input-mask, for one, declares react >=0.14.0, which React 19 satisfies, so the install goes through and the crash only shows up when the component mounts.

Credit Card Checkout V1 running live. A live 3D card that fills in as you type and flips for the CVC.

Step 1: find the package from the stack trace

The stack trace almost always names it. Look for the first line with node_modules/<package>/ in the path. In Next.js dev mode the error overlay shows the call stack. Library frames can be collapsed there, so expand them if you don't see a node_modules path.

If the trace only shows bundled chunk names, list every installed package whose code mentions findDOMNode:

bash
grep -rl --include='*.js' --include='*.cjs' --include='*.mjs' 'findDOMNode' node_modules \
  | grep -v '^node_modules/react-dom/' \
  | awk -F/ '{ print ($2 ~ /^@/) ? $2 "/" $3 : $2 }' | sort -u

That prints the top-level package that carries the call, so a match inside node_modules/a/node_modules/b shows up as a. A package that only mentions the name in an error message or a guarded fallback shows up too, so treat the list as suspects and let the stack trace decide. Then npm ls <package> tells you which of your own dependencies pulled it in.

Step 2: check the package against this table

Every row is a package we tested on October 26, 2026 in jsdom with react-dom/client. "Throws" means it threw on React 19.2.9. Where we re-ran it on React 18.3.1, it worked there.

Package (version)What React 19.2.9 didWhat to use instead
react-input-mask 2.0.4Threw reactDom.findDOMNode is not a function and rendered nothing. Rendered on React 18.3.1.A small typed mask hook
react-toast-notifications 2.5.1Threw findDOMNode is not a function when a toast was addedToast Stack, free, with an addToast shim
react-s-alert 1.4.1Threw render is not a function when an alert fired (ReactDOM.render is also gone)Same toast guide
react-notification-system 0.4.0Didn't mount: string refs, also removed in React 19Same toast guide
react-vis 1.12.1Threw Objects are not valid as a React child (found: object with keys {$$typeof, type, key, props, _owner, _store}). Rendered on React 18.3.1.Recharts, keeping your data
react-stripe-elements 6.1.2Rendered nothing and logged that it uses the "legacy childContextTypes API which was removed in React 19". Rendered on React 18.3.1.@stripe/react-stripe-js
react-sticky 6.0.3Threw Expected Sticky to be mounted within StickyContainer after legacy context warningsA sticky or hide-on-scroll navbar without it

Only the first two are findDOMNode errors. The others come from APIs React 19 removed in the same release (ReactDOM.render, string refs, legacy context), and they show up the same way: an old package, a fresh React 19 app and a crash on mount.

Not every old package breaks. These rendered on React 19.2.9 in the same test: react-text-mask 5.5.0, react-headroom 3.2.1, react-sparklines 1.7.0, react-card-flip 1.2.3, react-toggle 4.1.3, react-google-login 5.2.2, and react-flip-move 3.0.5 on first mount (we didn't test reordering). Some of them still have peer ranges that stop at React 18, so npm asks for --legacy-peer-deps to install them, but once installed they rendered.

If your package isn't in the table, search its issue tracker for "React 19" or "findDOMNode" before you decide. If it's still maintained, a newer major may already have the fix.

Step 3: fix it

You have three options, in the order I'd try them.

Replace the package

For an archived or unmaintained library this is the real fix. The links in the table go to our replacement guide for each one, and most need nothing paid.

Patch it to use a ref

If the package is small and you can't drop it yet, change the call yourself (for example with patch-package, or by copying the file into your project). The React docs give the pattern: put a ref on the element and read ref.current where the code used to call findDOMNode(this).

tsx
import { Component, createRef } from "react";

// Before (React 18 only):
//   componentDidMount() { (findDOMNode(this) as HTMLInputElement).select(); }
// After: give the element a ref and read it.
export class AutoselectInput extends Component<{ defaultValue?: string }> {
  input = createRef<HTMLInputElement>();

  componentDidMount() {
    this.input.current?.select();
  }

  render() {
    return <input ref={this.input} defaultValue={this.props.defaultValue} />;
  }
}

When the library used findDOMNode on children you pass in, so it has no element of its own to put a ref on, the React docs' third alternative is to wrap the children in a div the library owns. Measure the first element inside that div, which is the node findDOMNode(child) used to return. Measuring the div itself doesn't work: a block div is as wide as its parent, so its width doesn't follow the child. We ran the component below in Chromium with React 19.1.1: it reported the child's size, then reported again when the child grew wider and taller. The wrapper adds an element to your layout, so check your CSS after.

tsx
"use client";
import { useLayoutEffect, useRef, type ReactNode } from "react";

// A library that called findDOMNode(child) to measure whatever you passed in
// can wrap the children in a div it owns and measure that instead.
export function Measure({ children, onSize }: { children: ReactNode; onSize: (rect: DOMRect) => void }) {
  const box = useRef<HTMLDivElement>(null);

  useLayoutEffect(() => {
    // findDOMNode(child) returned the child's first DOM node, so measure that.
    // The div itself is as wide as its parent, so its own size wouldn't follow the child.
    const el = box.current?.firstElementChild ?? box.current;
    if (!el) return;
    const ro = new ResizeObserver(() => onSize(el.getBoundingClientRect()));
    ro.observe(el);
    return () => ro.disconnect();
  }, [onSize]);

  return <div ref={box}>{children}</div>;
}

In your own function components there's nothing to patch: on React 19 ref is a normal prop, so you can pass it straight to the element without forwardRef.

Pin React 18

This works only outside Next.js 15, whose upgrade guide sets React 19 as the minimum, and it postpones the work rather than removing it.

When the mask was for a card number

react-input-mask is the biggest package in the table, and a lot of its use is formatting card numbers. If that's your case, you may not need a mask at all. Stripe's card fields format the number, expiry and CVC themselves, and the digits never reach your code.

If you want the card form to look finished as well, our Credit Card Checkout V1 is a 3D card that fills in as you type and flips for the CVC, with Stripe's fields in its renderCardFields slot. It's a paid piece. The mask hook guide covers phone and date fields for free, and our post on credit card input formatting shows the free route for cards.

FAQ

Why do I get "findDOMNode is not a function" after upgrading to React 19?

A dependency still calls ReactDOM.findDOMNode, which React 19 removed. Find it in the stack trace, then replace the package or patch it to use a ref.

Can I add findDOMNode back to React 19?

No. React 19's react-dom doesn't export it. You can replace the package, patch its code, or stay on React 18.

Does --legacy-peer-deps fix findDOMNode errors?

No. That flag only changes what npm agrees to install. The crash happens later, when the component mounts.

Which packages break with "findDOMNode is not a function" on React 19?

In our October 2026 test, react-input-mask 2.0.4 and react-toast-notifications 2.5.1. react-s-alert, react-notification-system, react-vis, react-stripe-elements and react-sticky also failed on React 19, from other removed APIs.

Is findDOMNode deprecated or removed?

Both. It was deprecated in React 16.6 (October 2018) and removed in React 19.