website/docs/react/component-syntax.md
Components are the foundation for building UIs in React. Component Syntax is the standard way to write them in Flow: a dedicated component primitive, enabled by default, with several advantages over plain function components:
({name}: {name: string}) and the need to wrap props in Readonly<{...}> — component params are read-only by default.React.Node, and rejects components that implicitly return on any branch.renders clause that constrains what JSX shape the component is allowed to produce, enabling composition contracts across wrapper components and HOCs.this, no nested component definitions, and components can only be rendered as JSX — they cannot be called as plain functions. See Rules for Components below.:::info TypeScript comparison
component syntax is Flow-only. TypeScript models components as plain functions plus a props type. Flow's type-checker enforces rules that TypeScript's plain-function model leaves to convention or ESLint rules. See Flow's component syntax for the full comparison.
:::
You can declare a component with Component Syntax similar to how you'd declare a function:
import * as React from 'react';
component Introduction(name: string, age: number) {
return <h1>My name is {name} and I am {age} years old</h1>
}
You can use a component directly in JSX: <Introduction age={9} name="Mr. Flow" />.
There are a few important details to notice here:
children is the most common prop, and with Component Syntax it is just an ordinary parameter; declare it like any other. Its type is usually React.Node, which covers anything renderable (elements, strings, arrays, null, and more):
import * as React from 'react';
component Card(title: string, children: React.Node) {
return (
<section>
<h2>{title}</h2>
{children}
</section>
);
}
<Card title="Welcome">
<p>Hello!</p>
</Card>;
:::info TypeScript comparison
There is no need for a React.PropsWithChildren helper or for adding children to a props object type. children is just a parameter, and React.Node is the type to give it.
:::
Sometimes you want children to be more specific than "anything renderable". To restrict which elements can be passed, give children a render type instead of React.Node. For example, a Menu whose children must be MenuItems can require them with renders*:
import * as React from 'react';
component MenuItem(label: string) {
return <li>{label}</li>;
}
component Menu(children: renders* MenuItem) {
return <ul>{children}</ul>;
}
<Menu>
<MenuItem label="Home" />
<MenuItem label="Profile" />
</Menu>;
Render Types cover the full set of constraints: renders for a single required element, renders? for an optional one, and renders* for any number of children.
Components also allow you to rename parameters, which is useful when your parameter name is not a valid JavaScript identifier:
import * as React from 'react';
component RenamedParameter(
'required-renamed' as foo: number,
'optional-renamed' as bar?: number,
'optional-with-default-renamed' as baz?: number = 3,
) {
foo as number; // OK
bar as number | void; // OK
baz as number; // OK
return <div />;
}
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';
// star.js
component Star(color: string, size: number) {
return <div />;
}
type StarProps = React.PropsOf<Star>;
// blue_star.js
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:
import * as React from 'react';
component OtherComponent(foo: string, bar: number) {
return <div>{foo} {bar}</div>;
}
component FancyProps(
...props: {
...React.PropsOf<OtherComponent>,
additionalProp: string,
}
) {
return <OtherComponent foo={props.foo} bar={props.bar} />;
}
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:
import * as React from 'react';
type TextProps = {kind: 'text', text: string};
type ImageProps = {kind: 'image', src: string, alt: string};
type Props = TextProps | ImageProps;
component MediaItem(...props: Props) {
if (props.kind === 'text') {
return <span>{props.text}</span>;
} else {
return ;
}
}
Components allow you to declare optional parameters and specify defaults:
import * as React from 'react';
component OptionalAndDefaults(
color: string = "blue",
extraMessage?: string,
) {
let message = `My favorite color is ${color}.`;
if (extraMessage != null) {
message += `\n${extraMessage}`;
}
return <p>{message}</p>
}
<OptionalAndDefaults /> // No error, all of the parameters are optional!
The as operator also allows you to destructure your parameters:
import * as React from 'react';
component Destructuring(
config as {color, height}: Readonly<{color: number, height: number}>,
) { return <div /> }
Rest parameters can be destructured without using as:
import * as React from 'react';
type Props = Readonly<{ color: string, height: number }>;
component DestructuredRest(...{color, height}: Props) { return <div /> }
To access refs in components you just need to add a ref parameter.
import * as React from 'react';
component ComponentWithARef(ref: React.RefSetter<HTMLElement>) {
return <div ref={ref} />;
}
Behind the scenes, Component Syntax generates the right code for your target React version: when targeting React 18 it wraps the component in the required React.forwardRef call, and when targeting React 19 it passes ref through as an ordinary prop (React 19 no longer needs forwardRef). You declare the ref parameter the same way regardless of target. The one restriction for refs is that they must be defined as an inline parameter; refs within rest params are not supported, because Component Syntax needs to statically determine the ref from the component definition.
See Refs for more on typing refs, including the useRef hook, React.RefObject, React.RefSetter, callback refs, and reading ref.current.
Component Syntax enforces a few restrictions in components to help ensure correctness:
The return values must be a subtype of React.Node, otherwise React may crash while rendering your component.
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.
You cannot use this in a component.
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.
Components cannot be called as plain functions — they can only be rendered as JSX. Calling Foo(props) directly errors with [react-rule-call-component]. Use <Foo {...props} /> instead.
So these components are invalid:
import * as React from 'react';
component InvalidReturnValue() {
return new Object(); // ERROR: Value does not match `React.Node` type
}
component ImplicitReturn(someCond: boolean) { // ERROR
if (someCond) {
return <h1>Hello World!</h1>;
}
// ERROR: No return in this branch
}
component UsesThis() {
this.foo = 3; // ERROR: Accessing `this`
return null;
}
component Greeting() {
return <h1>Hello</h1>;
}
const elem = Greeting({}); // ERROR [react-rule-call-component]: use <Greeting /> instead
import typeof with Generic Components {#toc-import-typeof-generics}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 sidestep the eager-instantiation error at declaration time, use a namespace import typeof and index into it. Note that this collapses the polymorphism — the resulting type is treated as a concrete (non-polymorphic) component type, so you still cannot instantiate it with a type argument like CompType<number>:
import typeof * as GenericCompModule from './GenericComp';
type CompType = GenericCompModule['default']; // Declaration works, but CompType is no longer polymorphic
Component Syntax is enabled by default since Flow v0.317. On older versions, add component_syntax=true to the [options] section of your .flowconfig.