A minified React error is a normal React error with the message replaced by a number, because the production build strips the message strings to reduce bundle size. The number is a lookup key — paste it into React’s error decoder and you get the full text back.
That is the mechanical part and it takes ten seconds. The useful part is that a small number of error codes account for nearly everything you will see in production, and each has a characteristic cause that the decoded message only half explains.
Table of contents
- Decoding the number
- Error 130: element type is invalid
- Error 321: invalid hook call
- Errors 310 and 301: hooks and re-renders
- Error 418 and 425: hydration mismatches
- Getting readable errors in production
- How this fits the rest of the stack
- FAQ
Decoding the number
The error in your console looks like this:
Error: Minified React error #130; visit https://react.dev/errors/130 for the full message
The URL in the message is the decoder. Open it and you get the original text, with any interpolated values passed as query parameters.
If you have a code and no URL, the pattern is https://react.dev/errors/ followed by the number. Older builds point at reactjs.org/docs/error-decoder.html with an invariant query parameter; both resolve to the same table.
Worth knowing: the number is stable for a given React version but the table is versioned. If a code decodes to something that makes no sense for your error, check which React version produced it.
Error 130: element type is invalid
By a wide margin the most common one. Decoded, it reads: element type is invalid — expected a string or a class/function but got: undefined.
It means you rendered something that is not a component. Almost always an import problem, and there are three shapes:
- Default and named import mismatch. The module exports
export function Buttonand you wroteimport Button from './Button'. The default export is undefined. This is the single most frequent cause. - A circular import. Two modules import each other; one gets a partially-initialised module object where the component is still undefined. Characteristically intermittent — it depends on which module loads first, so it can appear only after an unrelated refactor changes the bundle order.
- A typo in the path resolving to a module without the export, which some bundlers report only at runtime.
The fast check is to log the import before rendering. If it prints undefined, you have found it and the fix is in the import line, not in the component.
The reason this one is so common in production specifically: development builds often paper over it with a clearer warning earlier in the render, while the minified build fails at the point of use.
Error 321: invalid hook call
Decoded: hooks can only be called inside the body of a function component. Three causes, and the third is the one that wastes an afternoon.
- A hook called conditionally or in a loop. Breaking the rules of hooks. Usually caught by the lint rule before it ships.
- A hook called outside a component, in a plain function or a class.
- Two copies of React in the bundle. Hooks are stored on a module-level dispatcher, so a component from one React copy calling a hook resolved by another finds nothing.
That third cause is the one to suspect when the code is obviously correct. It happens when a linked local package or a library bundles its own React, giving you two instances. Check for it:
npm ls react
More than one version in the tree is your answer. The fixes are to align versions, deduplicate, or declare react as a peer dependency in the offending package so it uses the host application’s copy.
Errors 310 and 301: hooks and re-renders
Error 310 — rendered more hooks than during the previous render. A hook is behind a condition, so the count changes between renders. React tracks hooks by call order, so a changing count means it cannot match state to hooks.
The usual shape is an early return that sits above some hooks:
// Broken: useEffect is skipped when loading is true
function Profile({ loading }) {
const [data, setData] = useState(null);
if (loading) return <Spinner />;
useEffect(() => { /* ... */ }, []);
}
Move every hook above every conditional return. All of them, unconditionally, at the top.
Error 301 — too many re-renders. A state update is running during render rather than in response to an event, so setting state triggers a render which sets state again. Nearly always a handler that is called instead of passed:
// Broken: calls handleClick during render
<button onClick={handleClick()}>Save</button>
// Correct: passes the function
<button onClick={handleClick}>Save</button>
The other version is calling a setter directly in the component body without a condition that eventually stops it.
Error 418 and 425: hydration mismatches
These appear only in server-rendered applications, and they mean the HTML the server produced does not match what the client rendered on first pass.
The reliable causes are all forms of the same thing — rendering something that differs between server and client:
- Dates and times.
new Date().toLocaleString()renders in the server’s timezone and then the browser’s. - Random values.
Math.random()or a generated id produces different output in each environment. - Browser-only APIs. Reading
windoworlocalStorageduring render, which has no value on the server. - Invalid HTML nesting. A
<div>inside a<p>. The browser silently restructures the DOM to make it valid, so the client tree no longer matches the server’s.
That last one is the sneaky one, because the mismatch is caused by the browser correcting your markup rather than by your code being non-deterministic. If the error makes no sense and nothing is time or random dependent, validate your nesting.
The correct fix for genuinely client-only content is to render it after mount rather than suppressing the warning — set a mounted flag in an effect and render the dynamic part only when it is true.
Getting readable errors in production
Decoding numbers by hand is fine for one error and useless for a stream of them from real users. The fix is source maps.
Generate them in your build, upload them to your error tracking service, and do not serve them publicly — a source map exposes your original source. Most build tools support generating maps as separate files that you upload during deploy and then delete from the output directory.
With source maps in place your error tracker shows the original file, line, and component stack rather than a minified frame, which turns most of this article into something you do not need to read.
Two things worth adding alongside:
- Error boundaries around meaningful sections of the tree. Without one, a single component throwing unmounts the whole application and the user gets a blank page. With one, they get a message and the rest of the app keeps working.
- A release identifier attached to reported errors, so you can tell whether a spike started with a specific deploy. This is the single most useful field for triage.
The release identifier is worth emphasising because most React errors in production correlate with a deploy. Knowing which one narrows the search to a diff instead of a codebase.
How this fits the rest of the stack
The pattern behind almost every one of these is that the error appears in production and not in development, which makes the deploy that introduced it the most valuable piece of information you have. Build logs and deploy history that sit alongside the running service turn on-or-after questions into a specific commit, and rolling back to the previous deploy is a way to stop the bleeding while you read the source map. Node services on RunxBuild covers deploying from a repository with that history intact, and the RunxBuild hosting calculator itemises what the frontend, API, and database cost together.
Useful related references:
- Generate a PDF from a React App: A Practical Walkthrough
- Deploy a React App for Free on RunxBuild: 2026 Guide
- Run React App: Local Preview, Production Build, and the Part People Skip
- Services on RunxBuild
FAQ
How do I decode a minified React error?
Open https://react.dev/errors/ followed by the error number. The page shows the full message with any interpolated values. The URL is also included in the error text React prints.
Why are React error messages minified in production?
To reduce bundle size. Full error strings add meaningful weight to a production build, so React replaces them with numeric codes that map to a lookup table hosted on its documentation site.
What causes React error 130?
Rendering something that is not a component — usually an import mismatch where a named export is imported as a default. Circular imports are the other common cause and tend to be intermittent.
What does invalid hook call mean when my code looks correct?
Usually two copies of React in the bundle. Hooks resolve through a module-level dispatcher, so a component from one copy cannot find hooks from another. Run npm ls react to check for duplicate versions.
How do I get readable stack traces in production?
Generate source maps during your build and upload them to your error tracking service rather than serving them publicly. Add error boundaries and a release identifier so errors can be tied to a specific deploy.