•about 3 years ago
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):Looks fine to me! But when we run it through the JSX transpiler... Our JSX transpiler doesn’t like that 🙁
(What’s happening behind the curtain here? JSX is turning all of our
React.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).
rendermethod return an array of nodes. If we return an array of elements, then React will transpile and render this just fine, _without a wrapper
(Remember how the JSX transpiler is just turning the
React.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.
Here’s the correct way to use a React Fragment: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
There’s also an alternate, more concise syntax for using React Fragments:
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-appdoesn’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:
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: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:But, we want four elements actually rendered to the page: 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: If we wrap the rendered content in
<div>s, then we’ll get the following rendered output: 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-areastyle set. Remember when we’re supposed to use React Fragments? Whenever we want to get rid of a
<div>. If we wrap our
ContentComponentin React Fragments instead of a
<div>: Then we’ll see a much different rendered output: 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-areastyle, 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!