loading...
Cover image for React's default way of appending itself to a DOM tree considered harmful

React's default way of appending itself to a DOM tree considered harmful

trumbitta profile image William Ghelfi ・4 min read

For the impatient

Alt Text

Don't do this

<body>
  <div id="root"></div>
</body>
const rootElement = document.getElementById('root');

ReactDOM.render(<App />, rootElement);

Do this instead

<body>
  <div data-role-react-root></div>
</body>
const rootElement = document.querySelector('[data-role-react-root]'),

ReactDOM.render(<App />, rootElement);

What am I talking about?

If you ever dabbled in React or followed even the most basic of its tutorials, chances are you'll find the following snippet quite familiar:

<!-- here reduced to the bare minimum for simplicity's sake -->
<body>
  <div id="root"></div>
</body>
const rootElement = document.getElementById("root");

ReactDOM.render(<App />, rootElement);

This <div id="root"></div> plus the accompanying JavaScript snippet, or one of their variants, are everywhere: tutorials, official docs, tools like Create React App and Nx, online samples... everywhere.

Here's why this is bad to the point of being harmful.

Why is it harmful

In a nutshell:

  • Ids and classes are meant to be used for styling purposes:
<div class="some-class"></div>
  • Even better, don't use ids for styling purposes; use them only for navigation:
<a href="/somepage#pricing">Pricing</a>

<!-- more code -->

<section id="pricing">
  <!-- awesome pricing table -->
</section>
  • When you need to add special meaning to HTML, use data attributes:
<div data-special>Such special, much data</div>

This separation of concerns (styling via classes, navigation via ids, special meaning via data attributes) helps avoiding some harmful situations you most certainly will find yourself in at some point in your career if you keep using <div id="root"></div> as a React root element.

Harmful situation #1

  • Sue creates a new React app.
<div id="root"></div>
  • Bob hops on the project. The design by Alan has a furry pink border for the main container, meaning Bob needs a selector and lo and behold #root is already there.
#root {
  border: 2rem furry pink;
}
  • John the CEO has a vision: furry borders are so 2005! We need to get rid of it!
  • John the CEO calls Alan at 5:00 in the morning on a sunday and asks him to change the border ASAP, before his meeting with prospect investors.
  • Alan, a designer with good enough CSS skills to get rid of a border, but no JavaScript nor React skill whatsoever, finds the offending CSS rule and deletes it.

Then, in a rare flash of love for his job, Alan the designer searches the codebase for places where id="root" is used.

Alan finds our friend <div id="root"></div>.

Alan thinks he just removed the one and only reason for it to exist, and deletes the whole line.

A sip of coffee, a deploy to production, and the job is done just before the important meeting.

Chaos ensues.

Harmful situation #2

  • Sue creates a new React app.
<div id="root"></div>
  • Bob starts working on the project, but leaves the company with a ton of work still to be done.
  • John the CEO decides to be smart and hires a bunch of junior developers from outer space.
  • Xyz, one of the junior devs from outer space, works on a new strategic feature and being the junior that she is, she uses document.getElementById('root') as the base for the strategic feature.
  • Something happens on Twitter and suddenly "root" is not a good word anymore. Sue changes the HTML:
  <div id="react-base"></div>
  • Sue also changes the React initialization code accordingly:
ReactDOM.render(<App />, document.getElementById('react-base'));
  • The strategic feature doesn't work anymore.

Chaos ensues.

Conclusions

Both of these harmful situations happened to me multiple times over the last 20 years.

At first, I was Alan or Xyz, more recently I was Bob or Sue.

We had the knowledge to avoid it. Something we learnt during the first months of our careers.

Something some old dude taught us and we forgot.

What's the best practice

When working with HTML, CSS, and JavaScript:

  • For styling purposes, use only classes and element selectors.
  • Use ids only for anchors and navigation.
  • When you need to get a DOM element via JavaScript, use data attributes.

Or, on the other hand:

  • If you see a class, assume it's for styling only.
  • If you see an id, assume it's for navigation only.
  • If you see a data attribute, assume it's used via JavaScript somehow, somewhere.

Separation of concerns.

Works wonders, if you remember to respect it.

Image credits

Posted on by:

Discussion

markdown guide
 

I totally agree, to separate concerns it's a best practice that all of us should follow, for more than one reason. To change or remove something that could be used elsewhere without first look for its usages can do serious damage; separation of concerns puts a limit on it, but also help us to simplify and isolate problems and makes easier to understand the codebase.

However, I wonder: is it possible that there are significant performance differences between getElementById and querySelector that make using the id instead of the data attribute a risk worth taking?

 

Loss of performance is certainly a factor in a JS application where you need to get dozens of elements or more. If that's the case, you should measure then optimize if needed. And an easy optimization is to use ids and getElementById().

The initialization code for a React app is just one querySelector() at the start, tho, and with a simple selector. I don't expect the difference in performance to be perceived by users of the app :)

 

Good article!

Go even deeper :D

const rootElement = document.body.appendChild(document.createElement('div'));

ReactDOM.render(<App />, rootElement);
 

I'll try this! Nobody in their right mind would touch it :D