Hi there! I’m Ken. I’m a Developer Advocate over at GetStream.io, where we build personalized and scalable activity feeds. For the last several months, I’ve been working on Winds 2.0, an open-source RSS reader and podcast listening app. It’s built in Node.js, Electron, Redux and React, and as of this writing, has over 5,000 stars on GitHub. If you’d like to check it out, head on over to https://getstream.io/winds/, or check out the source code at https://github.com/GetStream/winds. In Winds, we had a couple of unique frontend situations requiring the use of React Fragments. React Fragments are a neat little feature that were released late last year with React v16.2.0 — it’s a pretty small feature, but just knowing about the existence of them can save developers a huge headache when running into some very specific layout and styling situations.
Okay, so what’s a React Fragment?
Let’s back up a little — I’m sure that every React developer will have run into this at some point in their career (or will, very soon): https://gist.github.com/kenhoff/e4d4dad8059292748956400c2dec698e Looks fine to me! But when we run it through the JSX transpiler... https://gist.github.com/kenhoff/9c773e4ef503ddd1141e9625f62190cb Our JSX transpiler doesn’t like that 🙁
(What’s happening behind the curtain here? JSX is turning all of our
<div>
s and<MyComponent>
s intoReact.createElement()
calls — when the JSX transpiler sees multiple elements instead of a single element, it doesn’t know what tag name to render with. See React.createElement in the React documentation.)
So, what do we do? The same thing that we do every time we need to wrap a couple elements together, Pinky — wrap it in a <div>
! Just like web developers have been doing since the invention of the <div>
tag, another nested <div>
in the DOM won’t hurt anything (probably). https://gist.github.com/kenhoff/0f3030c8692958512c95b77965bbb755 Right, problem solved. But it turns out, there’s one more way to render this group of content on the page inside of a single React Component - by having the render
method return an array of nodes. https://gist.github.com/kenhoff/26c4601208edef855421f81b4ad34402 If we return an array of elements, then React will transpile and render this just fine, _without a wrapper MARKDOWN_HASHdc6dce4a544fdca2df29d5ac0ea9906bMARKDOWNHASH
. Neat!
(Remember how the JSX transpiler is just turning the
<div>
and<MyComponent>
tags intoReact.createElement()
calls? In this case, the transpiler is just putting together an array of those calls and affixing them directly to the parent element as children, as opposed to an array of uncontained elements that it can’t find a parent for. This feature got introduced with React v16.0.0.)
See, here’s the thing - Dan Abramov and the super duper smart folks on the React team looked at this and said: “Okay, so you can render an array of elements in two different ways - either by introducing an extra <div>
into the DOM, or by using some clunky non-JSX syntax. That doesn’t make for a good developer experience!” So, in v16.2.0, they released support for React Fragments.
Okay, now what’s a React Fragment?
Here’s the correct way to use a React Fragment: https://gist.github.com/kenhoff/845086e38420d44749bee2b74fa94306 Check this out - we write this just like we would the <div>
-wrapper method, but it’ll behave functionally equivalent to the array-render method, just with some nice JSX syntax. This will render those paragraph elements as an array, without any kind of wrapper <div>
.
There’s also an alternate, more concise syntax for using React Fragments:
class App extends React.Component { render() { return ( <> <p>I would</p> <p>really like</p> <p>to render</p> <p>an array</p> </> ); } }
Depending on your tooling, linters, build pipeline, etc, this might not work for you - the release notes say that wider support is on the way, but I’ve noticed
create-react-app
doesn’t support it yet.
Okay, but when do I actually use them?
Whenever you need to get rid of a wrapper <div>
. That’s it - if you ever find yourself in a situation where a wrapper <div>
is screwing up the layout of your React components, use a React Fragment. So, whenever you want to turn this: https://gist.github.com/kenhoff/d2eaccd9f3234315c9c41c1c9b821db1 Into this: https://gist.github.com/kenhoff/a3551c212810465c5de164bf6c253274
Example: 2x2 CSS grid
In Winds 2.0, we made pretty heavy use of CSS Grid. This is one of the general layouts that you’ll see when looking through podcasts or RSS feeds: If you don’t know CSS Grid yet, don’t worry - this bit of CSS will give you a quick idea of how things are laid out: https://gist.github.com/kenhoff/a55fc7b1ae4cce7a6b77ce428bc5dd3e Okay, let’s unpack this:
- In the upper left, we’ve got our brand / top-level navigation bits.
- In the lower left, we’ve got our “sub-navigation” - this can respond to a couple changes in global and local state, like “active” states, tabs, or collapsing navigation.
- On the right side, we’ve got the content that we’d like to show on the screen — in Winds, this is something like an RSS feed or article header, paired with an article list or article contents. These two sections will be a single React component - the props for both components change based on URL navigation.
All of these components interact with global (redux + URL) and local state slightly differently. This view is structured so that we’ve got three React components as siblings: https://gist.github.com/kenhoff/e2df06a908d31d7131192d6fcd96562a But, we want four elements actually rendered to the page: https://gist.github.com/kenhoff/c8f67182f3813e7fb996c41a68f5f6aa This….presents kind of a problem without React Fragments. Imagine that we’re creating the component that wraps the two right sections of our 2x2 grid view, the ContentComponent
: https://gist.github.com/kenhoff/d7c270e37338584267040d6166ce7f97 If we wrap the rendered content in <div>
s, then we’ll get the following rendered output: https://gist.github.com/kenhoff/90cafcb0bd6624e9c49c2231f735aa30 This won’t work - it will totally screw up the CSS grid. From the browser’s point of view, only 3 items are present inside the grid, and one of them doesn’t have a grid-area
style set. Remember when we’re supposed to use React Fragments? Whenever we want to get rid of a <div>
. If we wrap our ContentComponent
in React Fragments instead of a <div>
: https://gist.github.com/kenhoff/a28036a7f19b19430cb6d84bb4d33c42 Then we’ll see a much different rendered output: https://gist.github.com/kenhoff/fa712c6fcb14f43b96f1f17b31e60251 And that works exactly as expected! No wrapper <div>
is rendered, our 4 elements are rendered from 3 React components, the browser sees all elements with the correct grid-area
style, and our CSS Grid is rendered correctly.
Neat! What now?
React Fragments aren’t the most significant feature that’s shown up in React recently, but they are tremendously helpful in some specific situations. Just by knowing about the existence of React Fragments, you’ll save many hours of google-fu-induced headaches. They let us render an array of elements / components in a JSX-y way, which can solve a lot of layout and styling issues with tables, lists, and CSS grids. If you’re interested in seeing some of these React Fragments in a production app, check out the source code for Winds 2.0 over at https://github.com/GetStream/winds — or, you can download and use Winds 2.0 over at https://getstream.io/winds/. Until next time - cheers!