JavaScript and React course Β· Module 12: React and TypeScript
Typing Functional Components
In this lesson3
You built a ship module, connected it to the rest, and a week later a crew member passes it a number where it expected text. Nobody noticed, because the component had no operating manual. In React, every component is a function. TypeScript lets us precisely define what that function accepts (props) and what it returns (JSX). It's like an operating manual for every module on a spaceship.
React.FC vs Explicit Typing
There are two popular ways to type functional components in React. You'll meet both in other people's code, so it's worth understanding each before choosing yours.
Approach 1: React.FC (Function Component)
React.FC (short for React.FunctionComponent) is a generic type: you pass the props type in angle brackets and it describes the whole component function. You attach it to the constant, not to the parameter. To use the name React.FC you need to import React (or just the FC type).
1import React from 'react';
2
3// React.FC describes the whole component function (props + return type)
4const StarInfo: React.FC = () => {
5 return <p>Star information</p>;
6};Without a generic parameter, React.FC means a component with no props. When props are needed, we first describe them with a StarProps interface and then pass that interface in the angle brackets:
1// React.FC with props
2interface StarProps {
3 name: string;
4 brightness: number;
5}
6
7const StarCard: React.FC<StarProps> = ({ name, brightness }) => {
8 return (
9 <div>
10 <h3>{name}</h3>
11 <p>Brightness: {brightness}</p>
12 </div>
13 );
14};TypeScript now knows that name is a string and brightness a number, even though the destructuring itself has no types - they came from the annotation on the StarCard constant.
Approach 2: Explicit Typing (Recommended)
The second approach types the function parameter itself, exactly like any ordinary TypeScript function. You can write the return type as React.JSX.Element, meaning "a JSX element".
1// Explicit typing - more transparent and flexible
2interface StarProps {
3 name: string;
4 brightness: number;
5}
6
7function StarCard({ name, brightness }: StarProps): React.JSX.Element {
8 return (
9 <div>
10 <h3>{name}</h3>
11 <p>Brightness: {brightness}</p>
12 </div>
13 );
14}The same notation works in an arrow function - the props type still sits on the parameter, not on the constant:
1// Or as an arrow function
2const StarCardArrow = ({ name, brightness }: StarProps): React.JSX.Element => {
3 return (
4 <div>
5 <h3>{name}</h3>
6 <p>Brightness: {brightness}</p>
7 </div>
8 );
9};The JSX inside didn't change by a single character compared with approach 1. The only difference is where the type information lives.
Why is Explicit Typing Recommended?
The React community has largely moved away from React.FC for several reasons:
- React.FC used to add children - up to @types/react 17, every React.FC component "accepted"
children, even when it didn't use them. Since @types/react 18 that is no longer true, but many old articles still repeat it - Explicit typing is more readable - you can see exactly what the component accepts
- Better compatibility - it works identically with function and arrow function, and with generic components, which React.FC can't express
See what happens when you try to use children in both approaches in modern React:
1// React.FC - since @types/react 18 children is NOT added automatically
2const Badge: React.FC<{ label: string }> = ({ label, children }) => {
3 return <span>{label}{children}</span>; // ERROR: Property 'children' does not exist
4};
5
6// Explicit typing - children must be explicitly added
7interface BadgeProps {
8 label: string;
9}
10
11function Badge({ label }: BadgeProps): React.JSX.Element {
12 return <span>{label}</span>; // no children, because it's not in props
13}In both cases, if a component should accept children, you must add them to the props type - we'll do that in the next lesson. My advice: write explicit types on the parameter. It's shorter, clearer and matches the examples on react.dev.
Component Return Type
A React component can return different things: a JSX element, text, a number, or null when there's nothing to show. Each option has its own type:
1// React.JSX.Element - most commonly used
2function Planet(): React.JSX.Element {
3 return <div>Mars</div>;
4}
5
6// React.ReactElement - more general React element type
7function Planet(): React.ReactElement {
8 return <div>Mars</div>;
9}An important correction to older tutorials: writing JSX.Element without a prefix relied on the global JSX namespace, which was removed from the React 19 types. Write React.JSX.Element - it works in React 18 and 19. If a component can return null or text, use the widest type:
1// React.ReactNode - most flexible (JSX, string, number, null)
2function MaybeContent(): React.ReactNode {
3 return null; // can return null
4}
5
6// In practice, we most often omit the return type
7// TypeScript infers it automatically
8function Planet() {
9 return <div>Mars</div>; // TypeScript knows this is React.JSX.Element
10}A component returning React.ReactNode can be used in JSX since TypeScript 5.1. In daily work you usually just omit the return type and let the compiler infer it - typed props are the part that truly protects your ship.
Practice both styles in the editor. In the next lesson we'll extend the props interface with optional fields and children.
Code for this lesson: App.tsx
1import React from 'react';
2
3// Props interface
4interface CrewMemberProps {
5 name: string;
6 rank: string;
7 specialization: string;
8 yearsInSpace: number;
9}
10
11// Method 1: React.FC (Function Component)
12const CrewBadgeFC: React.FC<CrewMemberProps> = ({ name, rank, specialization, yearsInSpace }) => {
13 return (
14 <div className="badge-card">
15 <div className="badge-header">FC</div>
16 <h3>{name}</h3>
17 <p className="rank">{rank}</p>
18 <p>Specialization: {specialization}</p>
19 <p>Years in space: {yearsInSpace}</p>
20 </div>
21 );
22};
23
24// Method 2: Explicit typing (recommended)
25function CrewBadgeExplicit({ name, rank, specialization, yearsInSpace }: CrewMemberProps): JSX.Element {
26 return (
27 <div className="badge-card">
28 <div className="badge-header">Explicit</div>
29 <h3>{name}</h3>
30 <p className="rank">{rank}</p>
31 <p>Specialization: {specialization}</p>
32 <p>Years in space: {yearsInSpace}</p>
33 </div>
34 );
35}
36
37// Method 3: Arrow function with an explicit type
38const CrewBadgeArrow = ({ name, rank, specialization, yearsInSpace }: CrewMemberProps): JSX.Element => {
39 return (
40 <div className="badge-card">
41 <div className="badge-header">Arrow</div>
42 <h3>{name}</h3>
43 <p className="rank">{rank}</p>
44 <p>Specialization: {specialization}</p>
45 <p>Years in space: {yearsInSpace}</p>
46 </div>
47 );
48};
49
50function App() {
51 const member: CrewMemberProps = {
52 name: "Nova",
53 rank: "Captain",
54 specialization: "Navigation",
55 yearsInSpace: 15
56 };
57
58 return (
59 <div className="app">
60 <h1>Ways to type components</h1>
61 <div className="cards">
62 <CrewBadgeFC {...member} />
63 <CrewBadgeExplicit {...member} />
64 <CrewBadgeArrow {...member} />
65 </div>
66 <p className="note">All three approaches give the same result. The explicit typing approach (Explicit) is recommended.</p>
67 </div>
68 );
69}
70
71export default App;Remember: the props type is the module's operating manual - stick it to the parameter and every crew member knows how to plug it in.
Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What file extension is used for React components written in TypeScript?
2. Which approach to typing functional components is currently recommended by the React community?
Hands-on tasks in the game
- Code editor
Basic typing of interfaces and components
- Click in order
Arrange the syntax of an interface definition in TypeScript: