Element Types & Components
JSX is lowered into calls that package a type, props, children, and key into a React element. The element is a description whose type and props are carried forward.
JSX, createElement, and cloning converge on the same recognizable element construction path, while key, __self, and __source are handled separately from ordinary props.
Sources: packages/react/src/jsx/ReactJSXElement.js:170-283, packages/react/src/jsx/ReactJSXElement.js:610-746, packages/react/src/jsx/ReactJSXElement.js:748-764
Core concepts
React element
A React element is an object tagged by $$typeof with type, key, and props, plus development-only owner and debugging data.
Sources: packages/react/src/jsx/ReactJSXElement.js:170-283
Element type
The element type is the value passed as type; element creation preserves it and does not classify it as a class, function, or host component.
Sources: packages/react/src/jsx/ReactJSXElement.js:170-283, packages/react/src/jsx/ReactJSXElement.js:610-746
Reserved key
A key is normalized and stored on the element separately from ordinary props.
Sources: packages/react/src/jsx/ReactJSXElement.js:610-746, packages/react/src/jsx/ReactJSXElement.js:109-119
Children payload
Children are stored in props.children as one value for one child or an array when multiple child arguments are supplied.
Sources: packages/react/src/jsx/ReactJSXElement.js:610-746
Class component base
A class component instance receives props, context, refs, and an updater; the renderer later supplies the real updater.
Sources: packages/react/src/ReactBaseClasses.js:19-27
How JSX becomes an element
The JSX runtime has a development path that records owner/debug information and delegates to jsxDEVImpl. The implementation validates children, derives a key from the explicit argument or configuration, and then constructs the element through ReactElement.
return jsxDEVImpl(
type,
config,
maybeKey,
isStaticChildren,
debugStackDEV,
__DEV__ &&
(trackActualOwner
? createTask(getTaskName(type))
: unknownOwnerDebugTask),
);This call passes the JSX type, configuration, key, static-children mode, and development diagnostics into the implementation.
The production JSX path reuses the incoming configuration when no key was spread, otherwise copies properties while removing key, then calls ReactElement(type, key, props, getOwner(), undefined, undefined). In development, jsxDEVImpl checks whether static children are an array, validates each child, and freezes that array when possible.
createElement follows a similar shape but accepts children as positional arguments. It extracts key, copies non-reserved configuration properties into a new props object, assigns one child directly, or creates and freezes an array for multiple children.
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
if (__DEV__) {
if (Object.freeze) {
Object.freeze(childArray);
}
}
props.children = childArray;
}The important distinction is that multiple positional children become one array-valued props.children; a single child remains the original value.
This diagram shows the data movement from JSX entry points into the element object.
Evidence
- jsx-entrypackages/react/src/jsx/ReactJSXElement.js:441
- jsx-implementationpackages/react/src/jsx/ReactJSXElement.js:469
- element-constructorpackages/react/src/jsx/ReactJSXElement.js:170
- element-objectpackages/react/src/jsx/ReactJSXElement.js:188
Sources: packages/react/src/jsx/ReactJSXElement.js:441-467, packages/react/src/jsx/ReactJSXElement.js:469-604, packages/react/src/jsx/ReactJSXElement.js:291-348, packages/react/src/jsx/ReactJSXElement.js:489-509, packages/react/src/jsx/ReactJSXElement.js:685-701, packages/react/src/jsx/ReactJSXElement.js:170-283
What the element object contains
ReactElement treats props.ref as the source of truth for ref handling. In development, the object includes $$typeof, type, key, props, and _owner; in production, it includes $$typeof, type, key, ref, and props.
| Field | Meaning | Development detail |
|---|---|---|
$$typeof | Identifies the object as a React element. | Uses REACT_ELEMENT_TYPE. |
type | Carries the element type unchanged. | It is also passed to getTaskName when development debug tasks are created. |
key | Carries the normalized identity value. | Stored separately from props. |
props | Holds ordinary properties and children. | Frozen with the element when possible. |
_owner | Records the component responsible for creation. | Present in development only. |
These fields are established by ReactElement; development also adds _store, _debugInfo, _debugStack, and _debugTask, then freezes the props and element objects when supported. The debug task path obtains a name from type through getTaskName.
isValidElement recognizes an element by checking that the value is a non-null object whose $$typeof equals REACT_ELEMENT_TYPE. This is the creation-time validity check for the element marker, not a class/function/host classification.
Sources: packages/react/src/jsx/ReactJSXElement.js:170-237, packages/react/src/jsx/ReactJSXElement.js:188-200, packages/react/src/jsx/ReactJSXElement.js:240-279, packages/react/src/jsx/ReactJSXElement.js:441-467, packages/react/src/jsx/ReactJSXElement.js:29-48, packages/react/src/jsx/ReactJSXElement.js:891-897
How keys and child arrays are preserved
createElement validates each positional child in development before it assembles the props object. The JSX development runtime validates a static child array item by item and freezes it, while dynamic children are validated as one child value.
The creation path calls hasValidKey before extracting and stringifying a configuration key. Its definition treats a key as valid when config.key is not undefined, while ignoring its own development warning getter. createElement excludes key, __self, and __source from copied props. In development, defineKeyPropWarningGetter warns that key is not a prop.
Child traversal derives explicit keys from element.key; when no explicit key exists, it derives an implicit key from the child index. Explicit keys are escaped, while implicit keys use the index in base 36. The traversal machinery uses SEPARATOR and SUBSEPARATOR as path delimiters, and escapeUserProvidedKey protects user-provided key segments.
When mapping produces a new element, mapIntoArray calls cloneAndReplaceKey so the mapped child receives the relevant key path. cloneAndReplaceKey reconstructs the element with the same type and props but a replacement key.
This sequence shows the key-validation call order in createElement.
Evidence
- callerpackages/react/src/jsx/ReactJSXElement.js:610
- create-elementpackages/react/src/jsx/ReactJSXElement.js:610
- child-validationpackages/react/src/jsx/ReactJSXElement.js:864
- react-elementpackages/react/src/jsx/ReactJSXElement.js:170
Sources: packages/react/src/jsx/ReactJSXElement.js:610-631, packages/react/src/jsx/ReactJSXElement.js:489-509, packages/react/src/jsx/ReactJSXElement.js:656-665, packages/react/src/jsx/ReactJSXElement.js:109-119, packages/react/src/jsx/ReactJSXElement.js:667-681, packages/react/src/jsx/ReactJSXElement.js:121-141, packages/react/src/ReactChildren.js:74-93, packages/react/src/ReactChildren.js:32, packages/react/src/ReactChildren.js:33, packages/react/src/ReactChildren.js:63-65, packages/react/src/jsx/ReactJSXElement.js:748-764, packages/react/src/jsx/ReactJSXElement.js:610-746, packages/react/src/jsx/ReactJSXElement.js:864-882
Class, function, and host component boundaries
Element creation itself does not decide whether type represents a class component, function component, or host component. The constructor stores the supplied type on the element, while the creation path defers invalid-type handling because the renderer knows which types are valid for its environment.
The class contract visible here is the base Component initializer: it stores props and context, initializes refs, and installs either the supplied updater or ReactNoopUpdateQueue. PureComponent follows the same instance fields and updater setup, while its prototype is based on ComponentDummy. The supplied excerpts do not show the reconciler’s exact class-versus-function-versus-host classification branch.
Sources: packages/react/src/jsx/ReactJSXElement.js:170-193, packages/react/src/jsx/ReactJSXElement.js:610-625, packages/react/src/ReactBaseClasses.js:19-27, packages/react/src/ReactBaseClasses.js:132-138, packages/react/src/ReactBaseClasses.js:140
How it connects
The public react package exposes the element creation surface documented in Public API Reference. Element shape and $$typeof classification are detailed in react-is & Element Symbols.
The reconciler consumes the element description while building fibers, which is the boundary covered by Fiber Architecture and Render Phase. Its child-key behavior continues into Reconciliation Algorithm, where keys determine how child positions are compared.
Host-specific interpretation is renderer work: DOM element types connect to ReactDOM Host Config, while native element types connect to React Native Renderer (Fabric). Class lifecycle behavior is covered by Effects & Lifecycle.
Sources: packages/react/src/jsx/ReactJSXElement.js:170-283, packages/react/src/jsx/ReactJSXElement.js:610-746
Key takeaways
- React elements carry
$$typeof,type,key, andprops; development adds owner and debugging metadata. createElementstores one child directly and multiple children in a newly allocated array.keyis extracted from configuration and is not an ordinary prop.- Child traversal uses explicit keys or index-derived implicit keys, with escaping for path segments.
- The shown element-creation code preserves
type; it does not show the reconciler’s class, function, or host classification branch.