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

> "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.

- URL: https://qodesign.dev/blog/finddomnode-is-not-a-function-react-19
- Author: Teodor Avadani (https://qodesign.dev/blog/author/teodor-avadani)
- Published: 2026-10-26
- Updated: 2026-10-26
- Category: Next.js fixes
- Publisher: qodesign (https://qodesign.dev), a shop of animated React + Tailwind CSS components for Next.js, each sold with its full source code and an AI prompt.

`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](https://react.dev/blog/2024/04/25/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.

## 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 did | What to use instead |
|---|---|---|
| react-input-mask 2.0.4 | Threw `reactDom.findDOMNode is not a function` and rendered nothing. Rendered on React 18.3.1. | [A small typed mask hook](/react/react-input-mask-alternative) |
| react-toast-notifications 2.5.1 | Threw `findDOMNode is not a function` when a toast was added | [Toast Stack, free, with an addToast shim](/react/react-toast-notifications-alternative) |
| react-s-alert 1.4.1 | Threw `render is not a function` when an alert fired (`ReactDOM.render` is also gone) | [Same toast guide](/react/react-toast-notifications-alternative) |
| react-notification-system 0.4.0 | Didn't mount: string refs, also removed in React 19 | [Same toast guide](/react/react-toast-notifications-alternative) |
| react-vis 1.12.1 | Threw `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/react-vis-alternative) |
| react-stripe-elements 6.1.2 | Rendered 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/stripe-elements-card-form-nextjs) |
| react-sticky 6.0.3 | Threw `Expected Sticky to be mounted within StickyContainer` after legacy context warnings | [A sticky or hide-on-scroll navbar without it](/react/floating-navbar-react#react-sticky-react-19) |

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](/components/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](/react/react-input-mask-alternative) covers phone and date fields for free, and our post on [credit card input formatting](/blog/credit-card-input-format-brand-luhn-react) 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.
