One of the most common error messages while creating React apps is SyntaxError: Adjacent JSX elements must be wrapped in an enclosing tag.
We would get such an error if we try rendering MyComponent
.
const MyComponent = () => { return ( <h2>Hello...</h2> <p>World!</p> ); };
This happens because, under the hood, JSX elements are converted to HTML elements by calling React.createElement
.
React.createElement(type, [props], [...children])
For example, the following JSX element:
const elem = <div className="container">Hello world!</div>;
Is converted – by the Babel React plugin – to a Javascript function call:
const elem = React.createElement( "div", { className: "container" }, "Hello world!" );
Additionally, children nodes…
<div> <div>Child 1</div> <div>Child 2</div> </div>;
…are passed as optional ...children
arguments.
React.createElement( "div", null, React.createElement("div", null, "Child 1"), React.createElement("div", null, "Child 2") );
A single root element
However, see that while React.createElement
can accept an undefined amount of children nodes, it takes only a single root element – the type
argument. There lies the issue. Our example component:
const MyComponent = () => { return ( <h2>Hello...</h2> <p>World!</p> ); };
Would try to return two separate expressions, which is not a valid Javascript return
statement. Only a single expression can be returned.
const myComponent = () => { return ( React.createElement("h2", null, "Hello..."); React.createElement("p", null, "World!"); ); };
Enclosing in a single parent element
A straightforward solution to render MyComponent
, would enclose both elements inside a single wrapper.
const MyComponent = () => { return ( <div> <h2>Hello...</h2> <p>World!</p> </div> ); };
While this approach is correct, it has the disadvantage of creating an extra wrapper component. This can hurt the semantics of the page, invalidate CSS selectors, and create deeply nested <div>
trees for complex components.
React.Fragment
In order to avoid the above issues, we can wrap both elements in a custom React element, React.Fragment
.
const MyComponent = () => { return ( <React.Fragment> <h2>Hello...</h2> <p>World!</p> </React.Fragment> ); };
This will be transpiled to:
const MyComponent = () => { return React.createElement( React.Fragment, null, React.createElement("h2", null, "Hello..."), React.createElement("p", null, "World!") ); };
Which can then be used to render the native <h2>
and <p>
elements…
ReactDOM.render(<MyComponent />, document.getElementById('root'));
…Without any wrapper HTML element.
<div id="root"> <h2>Hello...</p2> <p>World!</p> </div>
However, what exactly is React.Fragment
?
Create a Fragment component
While the way React renders elements to the DOM is very intricate, we can emulate a Fragment
component quite easily. I found this approach in Maximilian Schwarzmüller’s React course.
The implementation is very simple. We create a custom component that simply returns its children
.
const MyFragment = (props) => props.children;
If we enclose MyComponent
children nodes inside the MyFragment
component, we’ll get the same result as using React.Fragment
.
const MyComponent = () => { return ( <MyFragment> <h2>Hello...</h2> <p>World!</p> </MyFragment> ); };
The <h2>
and <p>
elements will be rendered without any enclosing tag (besides the root
container).
<div id="root"> <h2>Hello...</p2> <p>World!</p> </div>