React component

Publish at:

When people start learning React, they often hear the same phrase again and again: "everything is a component". The phrase is useful, and it becomes much clearer once we ask a more precise question: what exactly counts as a component?

We will start one level lower, with React's createElement function[1]. Whatever React renders, createElement has to produce it.

That gives us a better first question:

What kind of JavaScript object does React actually want?

The method here is inspired by hole-driven development[2]. In the usual typed-language setting, the developer writes an incomplete program with explicit holes, asks the compiler what each hole requires, and then fills the holes one by one using the reported constraints. The useful part for us is the workflow itself: start with something incomplete, inspect the feedback, make the smallest justified change, and repeat.

React gives us runtime constraints we can inspect, and those constraints are enough to reconstruct a correct mental model.[a]

Smallest possible object #

root.render({});

Waiting to run

Not run yet.

React expects something more specific than a plain object. That already tells us something important:

React renders object-like values that follow React's element contract.

Guessing the shape #

We know createElement returns an object. That still leaves a big question: which fields?

If we sneak a quick look at the shape React itself works with, two field names show up immediately: type and props. For the smallest meaningful example, we can try a host tag and a text child. That leads to this next attempt:

root.render({
  type: 'div',
  props: {
    children: 'Hello'
  }
});

Waiting to run

Not run yet.

This attempt still misses part of the contract.

That is the next useful clue. The external shape looks plausible, and React is still looking for something more specific than "any object with type and props".

There must be something more specific about it.

What createElement returns #

React's public API gives us the answer. If we inspect that value:

const element = React.createElement('div', null, 'Hello');

console.log(element);

Waiting to run

Not run yet.

Two things matter immediately:

  • the result is a plain JavaScript object
  • React marks it internally as a real React element

If we look at React's source, this is the key idea: the element object is tagged with a special internal marker[3].

element = {
  $$typeof: REACT_ELEMENT_TYPE,
  // ...
}

That marker is what makes the object a React element and gives it a distinct meaning from a plain object with similar fields.

The React documentation describes the returned value as a React element object with a few important properties:

{
  type: 'div',
  props: {
    children: 'Hello'
  },
  key: null,
  ref: null
}

That shape is slightly simplified, but it is enough to see what React is modeling.

A React element is a plain JavaScript object that describes what React should render next.

Notice the wording carefully. A React element lives at the description layer. DOM nodes and components live at other layers in the rendering process.

Lookalike object #

If the element is an object, why did our hand-written object fail?

Because the real React element object combines the visible shape with an internal marker that identifies it as an element created by React.

Inside React's source code, element validation checks for an internal $$typeof field whose value identifies the object as a React element[3]. React uses that marker to distinguish a real React element from an arbitrary lookalike object.

If you want a public utility for asking questions like "is this a React element?" or "is this a valid React element type?", React also publishes the react-is package[4]. That package is useful for type checks, and it supports the same basic model we are building here: an element is a tagged object description, and a component is one possible value of an element's type.

The correct conclusion is:

React.createElement(...) is the public way to create a React element object.

First real element #

Now we can create the smallest useful element:

const element = React.createElement('div', null, 'Hello');

root.render(element);

Waiting to run

Not run yet.

This works.

At this point we know something precise:

  • root.render(...) renders a React element
  • a React element is an object
  • the object describes what should appear on the screen

So far we have an element whose type is the string 'div'.

That gives us a built-in host element. Components enter the picture when type holds a function or class.

From element type to component type #

The next step is small but important. In our examples, the type field can hold either a host tag like 'div' or a component type such as a function[5].

Here is the function case:

function Message() {
  return React.createElement('div', null, 'Hello');
}
const element = React.createElement(Message);

root.render(element);

Waiting to run

Not run yet.

This also works.

Now the meaning of the element changes:

{
  type: Message,
  props: {},
  key: null,
  ref: null
}

This object describes a component type. It tells React:

  1. here is a component type
  2. call it with the provided props
  3. then render whatever it returns

That gives us the missing definition.

So what is a React component? #

A React component is the value stored in the element's type when that value is a function or class that React knows how to use as a component type[6].

In this example:

function Message() {
  return React.createElement('div', null, 'Hello');
}

Message is the component.

In this example:

const element = React.createElement(Message);

element is the React element object, and its type points to the component.

That distinction is the foundation of React.

The minimal valid component #

What is the smallest valid component?

It is a function that React can use as a component type and whose return value is valid:

function Empty() {
  return null;
}

root.render(React.createElement(Empty));

Waiting to run

Not run yet.

This works because null is a valid render result, so the component successfully renders an empty result.

So the minimal valid component is a callable component type that returns a valid render result.

The layers React keeps separate #

At this point we can separate the three layers that beginners often mix together:

  • component: a function or class, such as Message
  • element: an object returned by React.createElement(...)
  • DOM node: the real browser node React creates later

Each term names a different layer in the rendering process.

That distinction gives us a precise sentence: components are callable types, and elements are the objects that reference them.

Final definition #

A React component is a function or class that can appear as the type of a React element. A React element is the object returned by React.createElement(...). It describes what React should render.

So if we ask, "what does React render?", the answer is:

React renders element objects.

And if we ask, "where does the component fit into that?", the answer is:

the component is one possible value of an element's type.

That is the first solid mental model to keep in your head before learning JSX, props, hooks, or state.

Summary #

We started with {} to locate React's boundary. Then we tried an object that looked more element-like and saw that React expects more than shape alone. Finally, we let React.createElement(...) produce the real object and worked backward from there.

That process gave us the answer:

  • React renders React element objects
  • React.createElement(...) returns a React element object
  • a React element is a description of what React should render next
  • a component is the function or class stored in the element's type
  • () => null or an equivalent function declaration is the minimal valid component

Notes

  1. The live demos in this article use the React 19.2.5 and react-dom 19.2.5 development bundles. Exact error messages and some runtime behavior may change in future React releases. · Back

References

  1. React `createElement` API reference (opens in a new tab) · Back
  2. GHC Typed Holes (opens in a new tab) · Back
  3. React source: `ReactJSXElement.js` (opens in a new tab) · Back
  4. React package: `react-is` (opens in a new tab) · Back
  5. React source: `ReactFiber.js` (opens in a new tab) · Back
  6. React source: `ReactBaseClasses.js` (opens in a new tab) · Back