Returning One UI Tree
A component call produces one JavaScript return value. That value can describe an entire nested UI tree.
function ChapterPage() {
return (
<main>
<h1>Components Before Abstractions</h1>
<p>Chapter 2</p>
</main>
);
}The returned value is the outer main element. Its props contain the heading and paragraph as children, so one JavaScript value carries the nested structure.
One Function Call Returns One Value
JavaScript ends a function call when it reaches return.
function ChapterPage() {
return <h1>Chapter 2</h1>;
}The caller receives the element value. Code after that statement is unreachable.
function ChapterPage() {
return <h1>Chapter 2</h1>;
console.log("never runs");
}React does not change JavaScript's return behavior. If a component needs to calculate a value, calculate it before the return.
function ChapterPage() {
const lessonCount = 6;
return <p>{lessonCount} lessons</p>;
}The local calculation runs during the component call. The returned React node then represents the UI for that result.
Adjacent JSX Needs One Grouping Value
This source places two expressions in one return position.
return (
<h1>Chapter 2</h1>
<p>Six lessons</p>
);The JSX transform cannot parse the adjacent elements as one JavaScript expression.
Use an HTML parent when the content has a document-level relationship.
return (
<header>
<h1>Chapter 2</h1>
<p>Six lessons</p>
</header>
);The component still returns one value. That value contains two child elements.
A fragment provides a React grouping value without adding a DOM parent. Do not reach for one automatically. A section, header, list, or other semantic parent often represents the content more accurately.
The rule requires one returned JavaScript value, not one visible DOM node.
A component can return a component element whose own result contains many nodes.
function App() {
return <ChapterPage />;
}App returns one element. React then calls ChapterPage and continues through its returned tree.
The Returned Tree Establishes Parent Relationships
Indentation helps humans read JSX, but element nesting establishes the parent and child structure.
function LessonList() {
return (
<section>
<h2>Lessons</h2>
<ul>
<li>Component Identity</li>
<li>Component Files</li>
</ul>
</section>
);
}The section contains the heading and list. The ul contains the two list items. React DOM creates the corresponding host relationships.
Move the closing ul and the tree changes. Whitespace indentation alone cannot preserve the old parent.
<ul>
<li>Component Identity</li>
</ul>
<li>Component Files</li>The second li is now outside the list and creates invalid document structure.
JSX checks syntax closure. It does not guarantee that the chosen HTML nesting is valid or accessible.
Use Valid HTML in the Tree
React lets components return host elements. The browser still applies HTML parsing and DOM rules.
This nesting is invalid because a paragraph cannot contain a div.
function LessonSummary() {
return (
<p>
Lesson summary
<div>Details</div>
</p>
);
}Use a parent that permits both pieces.
function LessonSummary() {
return (
<section>
<p>Lesson summary</p>
<div>Details</div>
</section>
);
}Invalid nesting can cause browser DOM correction and server hydration mismatches. React development builds can report selected nesting warnings, but source review should begin with correct HTML semantics.
Lists need list-item children.
<ul>
<li>Props as Inputs</li>
</ul>Tables need their supported table structure. Interactive controls should not be nested inside the same kind of interactive control.
React composition does not suspend those host rules.
Component Boundaries Can Be Invisible in the DOM
Break the tree into named components.
function ChapterPage() {
return (
<main>
<ChapterHeader />
<LessonList />
</main>
);
}Suppose ChapterHeader returns a header and LessonList returns a section. The browser DOM contains those host elements beneath main.
main
header
sectionThe DOM does not contain wrapper nodes for ChapterPage, ChapterHeader, or LessonList merely because those component functions exist.
React DevTools retains the component tree.
ChapterPage
ChapterHeader
LessonListThis separates the component without adding a DOM wrapper for every function. The component's returned host content determines the actual document nodes.
Return Text When Text Is the Complete Result
A component can return a string.
function CompletionLabel() {
return "Not started";
}When rendered inside a paragraph, React DOM creates a text node for the result.
<p><CompletionLabel /></p>A number can also be returned as text content.
function LessonCount() {
return 6;
}Do not wrap a value in a span only to satisfy an imagined requirement that components return elements. Add a host element when the document needs its semantics, style target, layout role, event behavior, or accessibility properties.
React can render strings, numbers, elements, arrays of renderable values, and empty results such as null. The surrounding tree determines where that result appears.
Return null for an Intentional Empty Result
A component can render no host content by returning null.
function DraftBadge({ published }) {
if (published) {
return null;
}
return <span>Draft</span>;
}React still has a DraftBadge component occurrence in its tree. The component is called, reads its input, and returns null. React DOM has no host node to create for that result.
Returning null does not skip component execution. It only produces empty rendered output for that call.
A component that returns null can still hold state and run supported Hooks. No DOM node is produced for the null result.
Returning null is useful when the correct result for the current inputs is no UI at all.
Make Missing Returns Visible in Source
A JavaScript function with no reached return produces undefined.
function LessonStatus({ complete }) {
if (complete) {
return <p>Complete</p>;
}
}When complete is false, the function reaches its end and returns undefined. React can treat undefined as empty output in a child position, so the page can become blank without a thrown exception.
Make the empty result explicit.
function LessonStatus({ complete }) {
if (!complete) {
return null;
}
return <p>Complete</p>;
}Or return UI for both cases.
return <p>{complete ? "Complete" : "In progress"}</p>;The explicit source tells a reviewer whether empty output was chosen or forgotten.
A blank page does not always produce an error. Trace which value the component returned for the current inputs.
Arrow Components Need a Returned Expression
An arrow function with an expression body returns that expression.
const ChapterTitle = () => <h1>Chapter 2</h1>;Adding braces creates a block body and requires return.
const ChapterTitle = () => {
return <h1>Chapter 2</h1>;
};This version returns undefined because the block contains no return statement.
const ChapterTitle = () => {
<h1>Chapter 2</h1>;
};The JSX expression is created and discarded inside the function body. React receives undefined from the component call.
Function declarations keep component names and bodies clear. Arrow components are also valid when a project convention uses them.
Do Not Return a DOM Node
This component creates a browser element manually.
function ChapterTitle() {
return document.createElement("h1");
}A DOM node is not a normal React node description. React needs elements, strings, numbers, empty values, or supported collections so it can manage rendering and compare later output.
Return JSX.
function ChapterTitle() {
return <h1>Chapter 2</h1>;
}Do not return a DOM node created with browser APIs from a component. A component returns React values. Carefully integrated imperative code uses a separate integration point.
Keep Render Calculation Free of External Writes
The returned tree should follow current inputs without changing external data.
function LessonCount({ lessons }) {
lessons.push({ title: "Extra lesson" });
return <p>{lessons.length}</p>;
}This mutates an array declared outside the component during rendering. A second render sees another added item, and another consumer sees changed data.
Calculate without mutation.
function LessonCount({ lessons }) {
const count = lessons.length;
return <p>{count}</p>;
}Building the returned tree should read its inputs rather than modify them.
Trace Reachable Output
Use the following component.
function ChapterPage() {
const title = "Components Before Abstractions";
return (
<main>
<h1>{title}</h1>
<LessonList />
</main>
);
}Trace it in order.
- React calls
ChapterPage. - The local
titlebinding is created. - JSX creates one outer element value with two children.
- The function returns that value.
- React follows the
LessonListcomponent child. - React DOM commits the reachable host result.
Only values reachable from the returned React node join the rendered tree. A JSX variable created but never included remains an unused JavaScript value.
const hiddenHeading = <h1>Unused</h1>;
return <main />;Lesson Check
The return rule can now be stated precisely.
one component call produces one JavaScript return value
that value can describe a nested React tree
the tree must use valid host nesting
null represents intentional empty output
unreached returns and discarded JSX can produce blank outputSource Notes
- React documentation, Your First Component
- React documentation, Writing Markup with JSX
- React documentation, Keeping Components Pure
- MDN, HTML elements reference