Most React TypeScript errors come from a missing or wrong type in one of three places: props, event handlers, and refs. React 19 also moved the JSX namespace, which produces its own error. Each has a small, specific fix, and the compiler message usually names the exact place.
Missing props types
Without a props type, TypeScript cannot know what a component accepts. It reports that the parameter has an implicit any type, or that a property does not exist on an empty object type.
interface ButtonProps {
label: string;
}
export default function Button({ label }: ButtonProps) {
return <button>{label}</button>;
}Annotating the parameter with ButtonProps fixes both messages, and passing a wrong prop now fails at the call site instead of inside the component. The interface keeps the component's contract in one place, so the error and its fix sit next to each other. See how to type React props with TypeScript for the full patterns.
Wrong event handler types
An inline handler often gets an implicit any event, which hides typos and loses autocomplete. Type the event with the matching React event type.
import type { ChangeEvent } from "react";
export default function NameInput() {
function handleChange(event: ChangeEvent<HTMLInputElement>) {
console.log(event.target.value);
}
return <input onChange={handleChange} />;
}The ChangeEvent type, parameterized with the input element, gives event.target the correct value type, so reading value no longer errors. See how to type React events and form handlers.
useRef and null
A ref typed for an input element starts as null, so its current property is the element or null and TypeScript forces a guard. React 19 also requires useRef to receive an argument.
import { useRef } from "react";
export default function FocusInput() {
const ref = useRef<HTMLInputElement>(null);
function focus() {
ref.current?.focus();
}
return (
<>
<input ref={ref} />
<button onClick={focus}>Focus</button>
</>
);
}The optional chaining on ref.current satisfies the null check. In React 19, refs are always mutable and useRef(undefined) returns a ref typed as the element or undefined, so a guard is still required before reading it.
The JSX namespace moved
The React 19 types removed the global JSX namespace in favor of React.JSX. Replace JSX.Element with React.JSX.Element.
function render(): React.JSX.Element {
return <p>Hello</p>;
}If you augment JSX for custom elements, wrap the augmentation in declare module "react" and use the module specifier your tsconfig JSX runtime expects. Older code that references the global JSX namespace breaks after the type upgrade.
Object is possibly null
TypeScript also flags a possibly null value when you use it without checking, which happens often with DOM elements and array lookups. Guard the value with an if statement or optional chaining before accessing its properties, and the error clears without a type assertion or any.
Verify the fix
Run the TypeScript compiler or the editor diagnostics after each fix and confirm the error clears. Most of these errors disappear with one precise annotation rather than a broad any, and that annotation documents the component for the next reader.
Rune AI
Key Insights
- Type props with an interface or type alias.
- Use React.ChangeEvent and React.MouseEvent for handlers.
- Guard ref.current because it can be null.
- Replace JSX.Element with React.JSX.Element.
- Prefer one precise annotation over any.
Frequently Asked Questions
Why did JSX.Element stop working after upgrading to React 19?
Why does useRef(null) complain about being read-only?
Conclusion
React TypeScript errors usually come from a missing props type, a wrong event type, an unguarded ref, or the moved JSX namespace. Each has one precise annotation as the fix.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.