React's Most Useful Types

Rate this content

We don't think of React as shipping its own types. But React's types are a core part of the framework - overseen by the React team, and co-ordinated with React's major releases.

In this live coding talk, we'll look at all the types you've been missing out on. How do you get the props type from a component? How do you know what ref a component takes? Should you use React.FC? And what's the deal with JSX.Element?

You'll walk away with a bunch of exciting ideas to take to your React applications, and hopefully a new appreciation for the wonders of React and TypeScript working together.

21 min
12 Dec, 2023


Sign in or register to post your comment.
  • Tewodros Birhanu
    Tewodros Birhanu
    Amazing Matt
  • Anuradha Kumari
    Anuradha Kumari
    What an amazing talk, I learned a lot, Thanks Matt!

AI Generated Video Summary

Today's Talk focuses on React's best types and JSX. It covers the types of JSX and React components, including React.fc and React.reactnode. The discussion also explores JSX intrinsic elements and react.component props, highlighting their differences and use cases. The Talk concludes with insights on using React.componentType and passing components, as well as utilizing the react.element ref type for external libraries like React-Select.

1. React's Best Types and JSX

Short description:

Today I'm going to be talking about React's best types. React and Types React are actually one thing, united. The major version releases are all synced together. Types React is very, very stable. Types are part of your API and your framework. Let's start by talking about the type of JSX. It's a React.jsx.element or JSX.element.

What's up friends, my name is Matt Pocock. I'm a full-time TypeScript educator. I'm pretty gutted that I can't be in Berlin to actually see you guys and say hello and stuff, but stuff. That means I've got to be here in the UK, but you can find my stuff at, go and support me, and, yeah, I'm excited to give this talk.

Today I'm going to be talking about React's best types. And you might think that's a kind of weird framing here, right? Because React itself doesn't ship its own types. It sort of just basically says, okay, you can install React here, and then, as a separate thing, we won't ship any types to you directly, but if you install Types React as well, then we're going to give you some types with it. And you might think, because of that, that the React team is sort of not really involved in the types. So you might think, sure, the community handles the Types React, and the React team handles React itself. But actually, it's more like this. It's more like the React team has stewardship over both of them. And actually, there are members of the React team that really just focus on the types and make sure that the types are correct. So you might think, sure, okay, the React and Types React, they're still separate things. But it's better to think of them as actually one thing, united. And that's because a lot of the decisions they make are synced together. The major version releases are all synced together. So if you have 17 on React, then Types React is also going to be 17, 18, etc. They also do ship patches. So Types React might ship a patch change without React shipping a change, but this means that they're really tied together. So Types React is very, very stable because really they consider any major change to the types to be something that React also needs a major version change for. So while React doesn't actually ship any types, it oversees its types very, very closely, and a lot of this is due to the really good work of Seb Silberman and a bunch of others. So it's important to think about the fact that types are features and types are part of your API. Types are part of your framework. And if you don't understand the types that come with React, you're not going to have a very good time using React with TypeScript. So this is my mission today is to teach you the most important types that React exposes and how you can use them to better power your apps and sort of just find your way around in React apps.

Let's start by talking about the type of JSX. If you have just a node up here, for instance, like this div, then what type does TypeScript infer this as? Well, if we hover over this node here, we can see that it's a React.jsx.element. You can also type this as just JSX.element. This is actually a relatively recent change. They've moved a lot of the global stuff that used to just be in JSX.element into a React namespace.

2. React JSX and React.reactnode

Short description:

So JSX.element represents a node of JSX, which can be a div with multiple div children. However, there are other things in React that can be rendered, such as strings, numbers, undefined, or null. These cannot be assigned to React.jsx.element, so the correct type to use is React.reactnode. React.reactnode represents all possible things that can be returned from a React component, including JSX, strings, numbers, and more.

So if you're using another project like solid or something in the same TypeScript TS config, it doesn't conflict. So JSX.element represents basically a node of JSX. It doesn't matter how many things are in that JSX node. So this could be a div with many other div children. It's always just React.jsx.element.

There's another type that goes along with this too, which is React.ReactElements, which is absolutely identical. In the React world, they both mean the same thing. And you might think that's really good. Now I understand every time I need to let's say type some children or something like that, I can use React.jsx.element and I'm all good to go. Except though, that there are lots of other things in React that you can render. It's not just like elements. You can render strings, you can render numbers, you can render undefined, or you can render null. All of these things are available to be returned from React components. And you can see by the number of errors here that these are basically not assignable to React.jsx.element. So you've probably seen at some point in your React career, type string is not assignable to type element or something along those lines. So in these situations, what is the correct type to use instead of React.jsx.element? Well, it's React.reactnode. React.reactnode, if we take a look at it, it contains string, number, boolean, React element, like a bunch of other stuff in here, React portal, null, or undefined. So it represents all of the possible things that you can return from a React component. This means if you need to type like a slot that can receive some JSX, like or a string or a number or anything that can be rendered to the DOM, then React.reactnode is the type that you need. Also 99% of the time, this is the type that you're going to want. I would actually just mostly ignore the fact that these types exist, React.jsx.element and basically just think of it as like, okay, we have some JSX here, what type does TypeScript need to give it? That's the type that it's gonna be. But in terms of actually using this, then actually like assigning types, actually using this within your application code, React.reactnode is the one that you are gonna need for using to represent JSX in all of its different forms.

3. React.fc and React.reactnode

Short description:

Now you might not have heard of React.reactnode before, but you probably will have heard of React.fc. It represents a functional component. React.fc makes sure that the thing you're returning is JSX and provides default props, display name, and other features. React.fc has changed over the years, becoming safer and no longer allowing children to be parsed into the component.

Now you might not have heard of React.reactnode before, but you probably will have heard of React.fc. This is one of the most famous types in really React and TypeScript, and it's also relatively controversial. A lot of people have strong opinions about this type.

It represents a functional component. You can either say React.fc or React.function component, because that's what it's short for. And you can basically use it in a few different ways. If you don't pass it any type arguments here, then it's a function without props, or a component without props. But you can pass an object, and that object becomes the prop. So you can see here I'm saying children is React.reactnode, and then the children appear inside here. If I were to remove this, then I would get an error here saying, property children does not exist on type empty objects.

And React.fc gives you a couple of different things. It gives you first of all, it makes sure that the thing that you're returning is JSX. So it forces you to return something. It says void is not assignable to type React node. Hopefully you understand what that means. And it means that you get default props on here, so you can actually assign some sort of default props to it if you want. But I think this API might be slightly deprecated, lightly deprecated. And display name is also something that comes along with this, too. So React.fc, it's kind of, it forces you to basically assign the type on the function itself, which we'll get to in a minute. And it also gives you a couple of things like display name and default props. React.fc has changed over the years. It used to, by default, allow you to parse in children to it. I can't remember which version this was, it was either React or 16 or 17, they changed this. And really like now, React.fc is a lot safer than it used to be. Because what used to happen is you would have a component here that never accepted any children, but it would an error here. Where now it says type children string has no properties in common with type intrinsic attributes. Basically saying you can't parse children into this slot. We can see here too how tied in the types of React are to the major releases. It actually took a major release of React to get this changed in React.fc. Because if you think about it, it's a pretty big breaking change to go from one version of React.fc, which just implicitly had children in it, to a version which doesn't.

4. React.fc and React.jsx.intrinsicelements

Short description:

React.fc used to return React.reactElement, but now it returns ReactNode. This means you can return anything you want from it. React.fc is a nice way to type your components. React.jsx.intrinsicelements is a type that contains all the different elements you can render into the DOM, with their respective props.

Another old weakness of React.fc is that it used to return React.reactElement. So if you used React.fc on this component, you would get an error saying that number is not assignable to ReactElement or JSXElementConstructor. Another very familiar error to those who worked with React.fc. But now React.fc actually returns ReactNode. So if we go and look at it here, we can see it's a function component that has a call signature that returns a ReactNode. Now we can return whatever we want from it. This means that all of the old complaints about React.fc no longer apply. It returns a ReactNode so you can return anything you want from it and no longer implicitly adds children in there either. So React.fc is actually a pretty nice way to type your components if you want to. For me though, personally, I just prefer to type the props object. I don't know why, I think this is just really muscle memory. It's a little easier further down the line if you want to change it into components, although that's relatively rare. For me I just prefer this syntax to this syntax where you add React.fc and you add the props in there. For some reason this just makes me feel warm and fuzzy. But if this one makes you feel more warm and fuzzy then I wouldn't remove this from a code base if I came across it. So, React.fc? Yep, it's fine to use. Go for it.

Next we're gonna talk about a global type in React called React.jsx.intrinsicelements. But the important thing to understand before this is that sometimes you want to extract all of the props from a certain native element. Up here we have some div props and this annotation here, or some might call it an incantation, is basically a way to get all of the div props, like the props that a div element receives, and just stick them in a type. This means that if you're creating your own native element wrapper then you can use div props and then extend it or do something with it. Here we're HTML attributes and then passing that a type argument of HTML div element. But this incantation can be pretty annoying because you've got to really like spool through a bunch of different stuff in order to get the one that you want. Often you're going to want a way to just auto complete your way to the right props type. And this is where this type called react.jsx.intrinsic elements comes in. This is basically a record of all of the different things. If I just go and look for interface intrinsic elements, extends global JSX intrinsic elements, extends GSX.intrinsic elements, you can see that on this huge great big interface, there are a big list of all of the elements that you can render into the DOM. So we have a, ABB, address, area, article aside, audio B. And what we have, they're the keys. And then on the values is we have the actual types that they receive, the props that they receive.

5. JSX Intrinsic Elements and react.component props

Short description:

So what I can do is add something cool and specify that it requires an ID string. This gives you an idea of how GSX intrinsic elements work. Another way to grab div props is to use react.jsx.intrinsic elements. In some situations, you'll want a list of all possible elements, which can be achieved using the key of react.jsx.intrinsic elements. Understanding this type is crucial in a react and TypeScript code base. Another way to grab native props is using the react.component props type helper. This has a use case for grabbing types from uncontrolled components.

So what I can do is I can actually add like something cool like this and just say, this requires an ID string. And now when I go back, or I do want to save, but not for long, I will come back and change that later. Now I can say const component equals like this and I can say return something cool and now something cool I'm getting an error because it requires the type of ID string and I have to pass it. So that gives you an idea of how these GSX intrinsic elements work.

So another way for me to grab these div props would be to say, okay, react.jsx.intrinsic elements. And then I basically index into it and I say, I want the div one of this and you can see that I can auto complete my way to it. So I just say div and boom it's there. This means I get to do a lot less thinking than when I just had react or HTML elements, HTML development, cause I need to remember all of those. Whereas here I can just remember, okay, it's on intrinsic elements. I'll just go and grab it.

In some situations too you're going to want a list of all of the possible elements that you can render to the dom. There are certain situations where this comes up and there are certain times in the react types as well that you'll see this too. And a really nice way to do this is basically to say all the possible element types are key of react.jsx.intrinsic elements. What this means is we take that entire object that we saw earlier and we basically say, give me the keys of that as a union. And it ends up with, if I just go and look at all of these, oh my God, do this funny dance again, I end up with a ABBA all of these keys inside a big union. And so if I say const example is all possible elements real, then I get all of the lists of keys. Very, very cool.

So react.jsx.intrinsic elements is one of the kind of most core things that you're going to see in a react and TypeScript code base. And understanding this type also will help you understand some of the weird errors that come up too. For those folks who are more experienced with react and TypeScript, you might have been shouting at your screen saying, what is this guy talking about? Because there's another way that you can grab the native props of an element. And that is using the react.component props type helper. We've got our div props from react.html elements. We've got our also div props from react.jsx.intrinsic elements. And then we've got also also div props, which are react.component props div. If we pass a string in here, we're going to get again, autocomplete of all of the native components. And it's going to give us basically the same results as if we had react.jsx.intrinsic elements. React.component props has another use case as well, which is let's say that you've got a select box up here or select component that you don't control. It's got some types here that aren't actually exported along with it. And we want to grab those types and put them in some props here. Well, we can just say react.component props and then pass in type of select.

6. JSX Intrinsic Elements vs. Component Props

Short description:

So JSX intrinsic elements are recommended over component props for better TypeScript performance. However, react.component props is useful for third-party components. Now, let's dive into a piece of code. We have input props extracted from react.jsx intrinsic elements and an input component that receives these props. The inputs are a record of different input components, such as text and number.

So it not only works with native elements, it also works with actual components. This is really cool and makes it just mega flexible. So you can basically just use it to say, okay, here's a component, give me the props from that component. Lovely.

The question remains though, like if you have a choice between these two, between JSX intrinsic elements and component props, which should you use? I would actually recommend using react.JSX intrinsic elements because basically this is something that's globally available anyway, and it's instead of passing it to a relatively complex type helper, if we go look at this, you can see it's actually doing some sort of crazy conditional type in here. So instead of doing that, you're just basically accessing something on an object that already exists in a global scope. So if you're worried about TypeScript performance, which is kind of like something that you probably should be thinking about a little bit if you're doing right in the weeds with TypeScript, then this is going to be the better solution because it's actually just simpler for TypeScript to process.

But react.component props. If you just stick with this new code base, then fair enough. It's nice to just have one way of doing things that's really, really simple. And it's mega useful if you have those third party components that you are not in control of, but you want the props from.

Let's jump next to a big piece of code here. I'm going to explain this piece by piece. First of all, we have some input props at the top, which we're extracting from react.jsx intrinsic elements. Then we have an input component, which is a react.fc being passed the input props. So an input component, that's going to be like a function that returns something can pass in some props, which are the input props. So like onChange and name and all that sort of stuff. Now, ignore this class for now. Let's go down to these inputs. These inputs are a record of string. So where the key string, and then the value is input component. So we have basically we have a text input here, which is saying input type text. And then we have a number down the bottom here, which is my number component. My number component is a class. It's a react class component that takes in React.or rather extends React.or component passing in the input props. So inside these input props, you can actually pass in the props. So if we look at this dot props, it's going to say it is basically the input props here. That's a big old readout there. Now, we understand the whole thing. We understand that this record is so these inputs are supposed to be a record of different input components.

7. React.componentType and Passing Components

Short description:

The correct type for this instead of fc is actually component type. React.componentType is a type helper that represents any React components, whether they are component classes or function components. This solves our error and allows us to pass both the MyNumber component and the text component into it.

The idea is we can say inputs.infact I'm going to move this to be a satisfies instead just so I can get autocomplete. Satisfies this. So inputs.number and text. We can see here that if we render these out and say inputs.number, then I can pass an on change to it like this and expect this to be correct for an input component. The issue we're getting, though, is that this number is saying type of my number component is not assignable to input component. That's because our type up here, React.fc is only really saying this can be a functional component. And classes are not functional components, they're something slightly different. So instead of React.fc, we want to be able to say any component can be passed into this slot and the correct type for this instead of fc is actually component type. So React.componentType is a type helper where if we take a look at it, it basically says either a component class or a function component. Just a lovely, nice, clean union between the two. And this P that's in this component type is representing the props of that. So it's a component class that receives these props or a function component that receives these props. And this solves our error. This means that text and number now, this MyNumber component, are both assignable to it. And if I changed this to component class instead, we'd end up with our React function components not being able to be passed to it. So React.componentType is a really nice way of representing any React components of the actual function, not the rendered JSX, that can be passed into a slot.

8. Passing Literal String to React.ComponentType

Short description:

We can pass a literal string as a member of a set of inputs and use the React.ComponentType to handle it. React can handle passing in a function, class, or native tag. We have the ElementType type that ensures the native tag corresponds to the input props. This is exposed from React and can be useful when working with ASPROP and doing polymorphic stuff in React.

Okay, for our next one, we've added a new requirement onto the code that we saw earlier. We now have a set of inputs here. And the set of inputs now has a new member. It has a member called base. And that base, instead of passing an actual function to this, we're just passing in the literal string, input. And we want our type of React.ComponentType to handle that. Now that seems like really crazy, because we're kind of doing two different things here. We're saying okay, either you can pass in a function, or you can pass in a class, or you can pass in a native tag. And it turns out that like React will actually handle this perfectly. So if we say const... Let's just render this out. Input.Base. It will let us do this, right? So it just means sure base string, and we've got a key here, but actually we want to be able to say okay, when we have this, we need to make sure that it's basically a native tag that can accept the props of input props. So are we shooting for the moon, or do we actually have a type that can help us? Yes we do. We have ElementType. And now all of these actually work. So basically, you can basically pass in input, but we can't pass in some sort of random tag in here. We can't pass in this, because it's got to be one of the JSX intrinsic elements that corresponds to the input props. I mean that's wild, right? That's wild that this works and that this is exposed from React. If we actually take a closer look at it here, so, here we have only tags that can receive SRC, and we're passing in basically SRC's string into Reactor ElementType. So if we hover over this you can see that we have AudioEmbedIframeImageInputScript, and then we also have a React.ComponentType at the end, with SourceString on it. This is all of the native tags, but it's also a React.ComponentType added on the end. This can be really useful when you're wrangling with the ASPROP and you're doing polymorphic stuff in React, and it's just a cool type to have lying around and really interesting to see how it works.

9. Using react.element ref type

Short description:

Let's imagine we're using an external library called React-Select and we want to pass a ref into that select. We specify the ref as HTMLSelectElement, but it gives us an error. To solve this, we can use react.element ref type of select to determine the correct type of ref for the select component.

I've got one more for you. Let's imagine that we're using an external library called, let's say, React-Select here, and we want to pass a ref into that select. And so we say, okay, the ref is going to be a HTMLSelectElement. That probably seems about right, but it's actually giving us an error here. And the error is pretty massive. HTMLSelectElement is missing the following types from type select. Oh god, oh god, oh god, it's really not having fun here. Wouldn't it be great if we could just say, okay, I want to know what type that select is from select. What we can do is we can say react.element ref type of select, and pass in select there. And now it's going to look at that select component, figure out what the type of ref is supposed to be and just put it in here.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Vue.js London 2023Vue.js London 2023
30 min
Stop Writing Your Routes
The more you keep working on an application, the more complicated its routing becomes, and the easier it is to make a mistake. ""Was the route named users or was it user?"", ""Did it have an id param or was it userId?"". If only TypeScript could tell you what are the possible names and params. If only you didn't have to write a single route anymore and let a plugin do it for you. In this talk we will go through what it took to bring automatically typed routes for Vue Router.
TypeScript Congress 2023TypeScript Congress 2023
31 min
Making Magic: Building a TypeScript-First Framework
I'll dive into the internals of Nuxt to describe how we've built a TypeScript-first framework that is deeply integrated with the user's IDE and type checking setup to offer end-to-end full-stack type safety, hints for layouts, middleware and more, typed runtime configuration options and even typed routing. Plus, I'll highlight what I'm most excited about doing in the days to come and how TypeScript makes that possible not just for us but for any library author.
React Advanced Conference 2021React Advanced Conference 2021
6 min
Full-stack & typesafe React (+Native) apps with
Why are we devs so obsessed with decoupling things that are coupled nature? tRPC is a library that replaces the need for GraphQL or REST for internal APIs. When using it, you simply write backend functions whose input and output shapes are instantly inferred in your frontend without any code generation; making writing API schemas a thing of the past. It's lightweight, not tied to React, HTTP-cacheable, and can be incrementally adopted. In this talk, I'll give a glimpse of the DX you can get from tRPC and how (and why) to get started.
TypeScript Congress 2022TypeScript Congress 2022
10 min
How to properly handle URL slug changes in Next.js
If you're using a headless CMS for storing content, you also work with URL slugs, the last parts of any URL. The problem is, content editors are able to freely change the slugs which can cause 404 errors, lost page ranks, broken links, and in the end confused visitors on your site. In this talk, I will present a solution for keeping a history of URL slugs in the CMS and explain how to implement a proper redirect mechanism (using TypeScript!) for dynamically generated pages on a Next.js website.

Add to the talk notes: 
TypeScript Congress 2023TypeScript Congress 2023
24 min
Faster TypeScript builds with --isolatedDeclarations
Type-checking a TypeScript codebase can be slow, especially for monorepos containing lots of projects that each need to use the type checker to generate type declaration files. In this talk, we introduce — for the very first time — a new TypeScript feature we are working on called “Isolated Declarations” that allows DTS files to be generated without using the type checker at all! This opens the door to faster declaration generation in TypeScript itself, as well as in external tools written in other languages such as ESBuild and swc. You'll see how to use this new option, and maybe (just maybe) you’ll be convinced about the benefits of explicit return types! Most importantly, we will show how Isolated Declarations enables parallel builds to spread work across your CPU cores to significantly improve the build speed of your TypeScript projects.

Workshops on related topic

React Advanced Conference 2021React Advanced Conference 2021
174 min
React, TypeScript, and TDD
Featured WorkshopFree
ReactJS is wildly popular and thus wildly supported. TypeScript is increasingly popular, and thus increasingly supported.

The two together? Not as much. Given that they both change quickly, it's hard to find accurate learning materials.

React+TypeScript, with JetBrains IDEs? That three-part combination is the topic of this series. We'll show a little about a lot. Meaning, the key steps to getting productive, in the IDE, for React projects using TypeScript. Along the way we'll show test-driven development and emphasize tips-and-tricks in the IDE.
React Advanced Conference 2022React Advanced Conference 2022
148 min
Best Practices and Advanced TypeScript Tips for React Developers
Featured Workshop
Are you a React developer trying to get the most benefits from TypeScript? Then this is the workshop for you.In this interactive workshop, we will start at the basics and examine the pros and cons of different ways you can declare React components using TypeScript. After that we will move to more advanced concepts where we will go beyond the strict setting of TypeScript. You will learn when to use types like any, unknown and never. We will explore the use of type predicates, guards and exhaustive checking. You will learn about the built-in mapped types as well as how to create your own new type map utilities. And we will start programming in the TypeScript type system using conditional types and type inferring.
TypeScript Congress 2022TypeScript Congress 2022
116 min
Advanced TypeScript types for fun and reliability
If you're looking to get the most out of TypeScript, this workshop is for you! In this interactive workshop, we will explore the use of advanced types to improve the safety and predictability of your TypeScript code. You will learn when to use types like unknown or never. We will explore the use of type predicates, guards and exhaustive checking to make your TypeScript code more reliable both at compile and run-time. You will learn about the built-in mapped types as well as how to create your own new type map utilities. And we will start programming in the TypeScript type system using conditional types and type inferring.
Are you familiar with the basics of TypeScript and want to dive deeper? Then please join me with your laptop in this advanced and interactive workshop to learn all these topics and more.
You can find the slides, with links, here:
And the repository we will be using is here:
TypeScript Congress 2023TypeScript Congress 2023
131 min
Practice TypeScript Techniques Building React Server Components App
In this hands-on workshop, Maurice will personally guide you through a series of exercises designed to empower you with a deep understanding of React Server Components and the power of TypeScript. Discover how to optimize your applications, improve performance, and unlock new possibilities.
During the workshop, you will:
- Maximize code maintainability and scalability with advanced TypeScript practices
- Unleash the performance benefits of React Server Components, surpassing traditional approaches
- Turbocharge your TypeScript with the power of Mapped Types
- Make your TypeScript types more secure with Opaque Types
- Explore the power of Template Literal Types when using Mapped Types
Maurice will virtually be by your side, offering comprehensive guidance and answering your questions as you navigate each exercise. By the end of the workshop, you'll have mastered React Server Components, armed with a newfound arsenal of TypeScript knowledge to supercharge your React applications.
Don't miss this opportunity to elevate your React expertise to new heights. Join our workshop and unlock the potential of React Server Components with TypeScript. Your apps will thank you.
TestJS Summit 2023TestJS Summit 2023
78 min
Mastering Node.js Test Runner
Node.js test runner is modern, fast, and doesn't require additional libraries, but understanding and using it well can be tricky. You will learn how to use Node.js test runner to its full potential. We'll show you how it compares to other tools, how to set it up, and how to run your tests effectively. During the workshop, we'll do exercises to help you get comfortable with filtering, using native assertions, running tests in parallel, using CLI, and more. We'll also talk about working with TypeScript, making custom reports, and code coverage.
Node Congress 2021Node Congress 2021
245 min
Building Serverless Applications on AWS with TypeScript
This workshop teaches you the basics of serverless application development with TypeScript. We'll start with a simple Lambda function, set up the project and the infrastructure-as-a-code (AWS CDK), and learn how to organize, test, and debug a more complex serverless application.
Table of contents:        - How to set up a serverless project with TypeScript and CDK        - How to write a testable Lambda function with hexagonal architecture        - How to connect a function to a DynamoDB table        - How to create a serverless API        - How to debug and test a serverless function        - How to organize and grow a serverless application

Materials referred to in the workshop:,HYgVepLIpfxrK4EQNclQ9w
DynamoDB blog Alex DeBrie:
Excellent book for the DynamoDB: