Skip to main content

Component Syntax

Components are the foundation for building UIs in React. While components are typically expressed using JavaScript functions, Component Syntax provides component primitive values that provide several advantages over function components, like:

  1. More elegant syntax with significantly less verbosity and boilerplate than functions
  2. Type system support tailored specifically for writing React
  3. Better support for React refs

Basic Usage

You can declare a component with Component Syntax similar to how you'd declare a function:

1import * as React from 'react';2
3component Introduction(name: string, age: number) {4  return <h1>My name is {name} and I am {age} years old</h1>5}

You can use a component directly in JSX: <Introduction age={9} name="Mr. Flow" />.

There are a few important details to notice here:

  1. the prop parameter names declared in the Introduction component are the same as the prop names passed to Introduction in JSX
  2. the order of the parameters in the declaration does not need to match the order that they are provided in JSX

Parameters

String Parameters/Renaming Parameters

Components also allow you to rename parameters, which is useful when your parameter name is not a valid JavaScript identifier:

1import * as React from 'react';2
3component RenamedParameter(4  'required-renamed' as foo: number,5  'optional-renamed' as bar?: number,6  'optional-with-default-renamed' as baz?: number = 3,7) {8  (foo: number); // OK9  (bar: number | void); // OK10  (baz: number); // OK11
12  return <div />;13}

Rest Parameters

Sometimes you do not want to list out every prop explicitly because you do not intend to reference them individually in your component. This is common when you are writing a component that wraps another and need to pass props from your component to the inner one:

import * as React from 'react';

import type {Props as StarProps} from './Star';
import Star from './Star';

component BlueStar(...props: StarProps) {
return <Star {...props} color="blue" />;
}

Rest parameters use an object type as an annotation, which means you can use existing type utilities like object spreads and Pick to annotate more complex prop patterns:

1import * as React from 'react';2
3component OtherComponent(foo: string, bar: number) {4  return foo + bar;5}6
7component FancyProps(8  ...props: {9    ...React.PropsOf<OtherComponent>,10    additionalProp: string,11  }12) {13  return <OtherComponent foo={props.foo} bar={props.bar} />;14}

Disjoint Union Props

You can use a rest parameter with a disjoint union type to define a component that accepts one of several prop shapes. Refine the props inside the component body using the discriminant field:

1import * as React from 'react';2
3type TextProps = {kind: 'text', text: string};4type ImageProps = {kind: 'image', src: string, alt: string};5type Props = TextProps | ImageProps;6
7component MediaItem(...props: Props) {8  if (props.kind === 'text') {9    return <span>{props.text}</span>;10  } else {11    return <img src={props.src} alt={props.alt} />;12  }13}

Optional Parameters and Defaults

Components allow you to declare optional parameters and specify defaults:

1import * as React from 'react';2
3component OptionalAndDefaults(4  color: string = "blue",5  extraMessage?: string,6) {7  let message = `My favorite color is ${color}.`;8  if (extraMessage != null) {9    message += `\n${extraMessage}`;10  }11  return <p>{message}</p>12}13
14<OptionalAndDefaults /> // No error, all of the parameters are optional!

Destructuring Parameters

The as operator also allows you to destructure your parameters:

1import * as React from 'react';2
3component Destructuring(4  config as {color, height}: Readonly<{color: number, height: number}>,5) { return <div /> }

Rest parameters can be destructured without using as:

1import * as React from 'react';2
3type Props = Readonly<{ color: string, height: number }>;4
5component DestructuredRest(...{color, height}: Props) { return <div /> }

Ref Parameters

To access refs in components you just need to add a ref parameter.

1import * as React from 'react';2
3component ComponentWithARef(ref: React.RefSetter<HTMLElement>) {4  return <div ref={ref} />;5}

Behind the scenes Component Syntax will wrap the component in the required React.forwardRef call to ensure the component works as expected at runtime. The one restriction for refs is they must be defined as an inline parameter, refs within rest params are not supported. This is due to the need to compile in the forwardRef call, for this to work correctly we need to be able to statically determine the ref from the component definition.

Rules for Components

Component Syntax enforces a few restrictions in components to help ensure correctness:

  1. The return values must be a subtype of React.Node, otherwise React may crash while rendering your component.

  2. All branches of a component must end in an explicit return. Even though undefined is a valid return value, we've seen many instances where an explicit return would have prevented bugs in production.

  3. You cannot use this in a component.

  4. You cannot define a component inside another component or a hook. This triggers the nested-component lint, because a nested component gets a new identity on every render, causing React to unmount and remount it each time. Use a regular function instead if the inner "component" doesn't use hooks, or move the component to the top level.

So these components are invalid:

1import * as React from 'react';2
3component InvalidReturnValue() {4  return new Object(); // ERROR: Value does not match `React.Node` typeincompatible-typeCannot return new Object() because: Either Object [1] is incompatible with ExactReactElement_DEPRECATED [2]. Or Object [1] is incompatible with React.Portal [3]. Or property @@iterator is missing in Object [1] but exists in $Iterable [4].5}6
7component ImplicitReturn(someCond: boolean) { // ERRORcomponent-missing-returnCannot declare component because component ImplicitReturn [1] is not guaranteed to reach a return statement. An explicit return statement must be included for all possible branches.8  if (someCond) {9    return <h1>Hello World!</h1>;10  }11  // ERROR: No return in this branch12}13
14component UsesThis() {15  this.foo = 3; // ERROR: Accessing `this`component-this-referenceCannot reference this from within component declaration [1]prop-missingCannot assign 3 to this.foo because property foo is missing in global object [1].16  return null;17}

import typeof with Generic Components

When you use import typeof on a generic component's default export, the imported type alias exposes the generic as a required type argument. This means you cannot use the imported type without supplying the argument explicitly:

// GenericComp.js
import * as React from 'react';

component MyComp<T>(data: T, render: (item: T) => React.Node) {
return render(data);
}

export default MyComp;

// Consumer.js
import typeof MyComp from './GenericComp';

type CompType = MyComp; // Error: missing 1 type argument

To preserve the polymorphism, use a namespace import typeof and index into it:

import typeof * as GenericCompModule from './GenericComp';

type CompType = GenericCompModule['default']; // Works

Enable Component Syntax

In your .flowconfig, under the [options] heading, add component_syntax=true.

See Also

  • Generics — using generic type parameters in component props
  • Variance — understanding read-only props and covariance