DEV Community

Cover image for Forwarding refs in TypeScript
selbekk
selbekk

Posted on • Originally published at selbekk.io

4 1

Forwarding refs in TypeScript

Originally posted on my blog - selbekk.io

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} />
  )
);
Enter fullscreen mode Exit fullscreen mode

You can then use this component like ref was a regular prop:

const buttonRef = React.useRef();
return (
  <Button ref={buttonRef}>
    A button
  </Button>
);
Enter fullscreen mode Exit fullscreen mode

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-wrapped component is:

type Props = {};
const Button = React.forwardRef<HTMLButtonElement, Props>(
  (props, ref) => <button ref={ref} {...props} />
);
Enter fullscreen mode Exit fullscreen mode

Or more generally:

const MyComponent = React.forwardRef<
  TheReferenceType, 
  ThePropsType
>((props, forwardedRef) => (
  <CustomComponentOrHtmlElement ref={forwardedRef} {...props} />
));
Enter fullscreen mode Exit fullscreen mode

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} />
);
Enter fullscreen mode Exit fullscreen mode

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!

Billboard image

Imagine monitoring that's actually built for developers

Join Vercel, CrowdStrike, and thousands of other teams that trust Checkly to streamline monitor creation and configuration with Monitoring as Code.

Start Monitoring

Top comments (0)

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

👋 Kindness is contagious

Immerse yourself in a wealth of knowledge with this piece, supported by the inclusive DEV Community—every developer, no matter where they are in their journey, is invited to contribute to our collective wisdom.

A simple “thank you” goes a long way—express your gratitude below in the comments!

Gathering insights enriches our journey on DEV and fortifies our community ties. Did you find this article valuable? Taking a moment to thank the author can have a significant impact.

Okay