DEV Community

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

Posted on

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

For the impatient

Alt Text

Don't do this

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

ReactDOM.render(<App />, rootElement);
Enter fullscreen mode Exit fullscreen mode

Do this instead

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

ReactDOM.render(<App />, rootElement);
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode
const rootElement = document.getElementById("root");

ReactDOM.render(<App />, rootElement);
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode
  • 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>
Enter fullscreen mode Exit fullscreen mode
  • When you need to add special meaning to HTML, use data attributes:
<div data-special>Such special, much data</div>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode
  • 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;
}
Enter fullscreen mode Exit fullscreen mode
  • 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>
Enter fullscreen mode Exit fullscreen mode
  • 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>
Enter fullscreen mode Exit fullscreen mode
  • Sue also changes the React initialization code accordingly:
ReactDOM.render(<App />, document.getElementById('react-base'));
Enter fullscreen mode Exit fullscreen mode
  • 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

Top comments (7)

Collapse
 
gargante profile image
Gargante

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?

Collapse
 
trumbitta profile image
William Ghelfi • Edited

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 :)

Collapse
 
amplanetwork profile image
Ampla Network

html.spec.whatwg.org/multipage/dom...

There is clearly nothing harmful at all. Ids are made to direct targeting a UNIQUE element in the dom. There is nothing in the specification that prevent anyone to use it, especially when scripting.

The way you want to separate CSS from the code is ok, but don't say something is harmfull just without any real materials.

Modifying the production server in the middle of the night for CSS purpose is Harmfull, not targeting Id. This is only your opinion, no real proof of any kind in any official documentation. Your example makes no sense.

No one do that in real life. And if you delete the line ... You delete attribute too.

Collapse
 
iamandrewluca profile image
Andrei L • Edited

This is only your opinion

What you wrote here is only your opinion also ))
Have you ever experienced making a page builder in React? You will be amazed how much hacks you have to do, to make it work. So yes, this use case is valid one and makes sense.

<div data-role-react-root="block-1"></div>
<div data-role-react-root="other-block"></div>
<div data-role-react-root="block-1"></div>
<div data-role-react-root="some-other-block"></div>

<!-- block-1.js -->
<script>
    document.querySelectorAll('[data-role-react-root="block-1"]').forEach(rootElement => {
        const Block1 = () => React.createElement('div', { children: ['my block 1']})
        ReactDOM.render(React.createElement(Block1), rootElement);
    })
</script>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
trumbitta profile image
William Ghelfi

Wow, dude. So much hate.

Collapse
 
iamandrewluca profile image
Andrei L • Edited

Good article!

Go even deeper :D

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

ReactDOM.render(<App />, rootElement);
Collapse
 
trumbitta profile image
William Ghelfi

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