meAmidanote

01. Function Components

These can be written as normal functions that take a props argument and return a JSX element.


// Declaring type of props - see "Typing Component Props" for more examples
type AppProps = {
  message: string;
}; /* use `interface` if exporting so that consumers can extend */

// Easiest way to declare a Function Component; return type is inferred.
const App = ({ message }: AppProps) => <div>{message}</div>;

// you can choose annotate the return type so an error is raised if you accidentally return some other type
const App = ({ message }: AppProps): JSX.Element => <div>{message}</div>;

// you can also inline the type declaration; eliminates naming the prop types, but looks repetitive
const App = ({ message }: { message: string }) => <div>{message}</div>;

Why is `React.FC` discouraged? What about `React.FunctionComponent`/`React.VoidFunctionComponent`?

You may see this in many React+TypeScript codebases:


const App: React.FunctionComponent<{ message: string }> = ({ message }) => (
  <div>{message}</div>
);

However, the general consensus today is that React.FunctionComponent (or the shorthand React.FC) is discouraged. This is a nuanced opinion of course, but if you agree and want to remove React.FC from your codebase, you can use this jscodeshift codemod.

Some differences from the "normal function" version:

  • React.FunctionComponent is explicit about the return type, while the normal function version is implicit (or else needs additional annotation).

  • It provides typechecking and autocomplete for static properties like displayName, propTypes, and defaultProps.

    • Note that there are some known issues using defaultProps with React.FunctionComponent. See this issue for details. We maintain a separate defaultProps section you can also look up.
  • It provides an implicit definition of children (see below) - however there are some issues with the implicit children type (e.g. DefinitelyTyped#33006), and it might be better to be explicit about components that consume children, anyway.


const Title: React.FunctionComponent<{ title: string }> = ({
  children,
  title,
}) => <div title={title}>{children}</div>;

Using `React.VoidFunctionComponent` or `React.VFC` instead

As of @types/react 16.9.48, you can also use React.VoidFunctionComponent or React.VFC type if you want to type children explicitly. This is an interim solution until FunctionComponent will accept no children by default (planned for @types/react@^18.0.0).


type Props = { foo: string };

// OK now, in future, error
const FunctionComponent: React.FunctionComponent<Props> = ({
  foo,
  children,
}: Props) => {
  return (
    <div>
      {foo} {children}
    </div>
  ); // OK
};

// Error now, in future, deprecated
const VoidFunctionComponent: React.VoidFunctionComponent<Props> = ({
  foo,
  children,
}) => {
  return (
    <div>
      {foo}
      {children}
    </div>
  );
};

  • In the future, it may automatically mark props as readonly, though that's a moot point if the props object is destructured in the parameter list.

In most cases it makes very little difference which syntax is used, but you may prefer the more explicit nature of React.FunctionComponent.

Minor Pitfalls

These patterns are not supported:

Conditional rendering


const MyConditionalComponent = ({ shouldRender = false }) =>
  shouldRender ? <div /> : false; // don't do this in JS either
const el = <MyConditionalComponent />; // throws an error

This is because due to limitations in the compiler, function components cannot return anything other than a JSX expression or null, otherwise it complains with a cryptic error message saying that the other type is not assignable to Element.


const MyArrayComponent = () => Array(5).fill(<div />);
const el2 = <MyArrayComponent />; // throws an error

Array.fill

Unfortunately just annotating the function type will not help so if you really need to return other exotic types that React supports, you'd need to perform a type assertion:


const MyArrayComponent = () => (Array(5).fill(<div />) as any) as JSX.Element;

See commentary by @ferdaber here.