Forwarding refs in TypeScript
Learn how you can forward refs in React and TypeScript!
When you're working on a component library, or just creating reusable components in general, you often end up creating small wrapper components that only adds a css class or two. Some are more advanced, but you still need to be able to imperatively focus them.
This used to be a hard problem to solve back in the days. Since the ref prop is treated differently than others, and not passed on to the component itself, the community started adding custom props named innerRef
or forwardedRef
. To address this, React 16.3 introduced the React.forwardRef
API.
The forwardRef API is pretty straight-forward. You wrap your component in a function call, with is passed props and the forwarded ref, and you're then supposed to return your component. Here's a simple example in JavaScript:
const Button = React.forwardRef(
(props, forwardedRef) => (
<button {...props} ref={forwardedRef} />
)
);
You can then use this component like ref
was a regular prop:
const buttonRef = React.useRef();
return (
<Button ref={buttonRef}>
A button
</Button>
);
How to use forwardRef with TypeScript
I always screw this up, so I hope by writing this article I can help both you and me to figure this out.
The correct way to type a forwardRef component is:
type Props = {};
const Button = React.forwardRef<HTMLButtonElement, Props>(
(props, ref) => <button ref={ref} {...props} />
);
Or more generally:
const MyComponent = React.forwardRef<
TheReferenceType,
ThePropsType
>((props, forwardedRef) => (
<CustomComponentOrHtmlElement ref={forwardedRef} {...props} />
));
It was a bit un-intuitive at first, because it looks like you can pass a regular component to ForwardRef
. However, regular components don't accept a second ref parameter, so the typing will fail.
I can't count how often I've done this mistake:
type Props = {};
const Button: React.RefForwardingComponent<
HTMLButtonElement,
Props
> = React.forwardRef(
(props, ref) => <button ref={ref} {...props} />
);
This is a mistake, because the RefForwardingComponent
is the type of the render function you create (the one that receives props
and ref
as arguments), and not the result of calling React.forwardRef
.
In other words - remember to pass your type variables directly to React.forwardRef
! It will automatically return the correct type for you.
Another gotcha is the order of the type variables - it's the ref type first, then the props type. It's kind of counter-intuitive to me, since the arguments to the render function is the opposite (props, ref) - so I just remember it's the opposite of what I'd guess. 😅
I hope this article helped you figure out this pesky typing issue that have gotten me so many times in a row. Thanks for reading!