React: React Fire: Modernizing React DOM

Created on 31 Aug 2018  ·  227Comments  ·  Source: facebook/react


For latest status, see an update from June 5th, 2019: https://github.com/facebook/react/issues/13525#issuecomment-499196939


This year, the React team has mostly been focused on fundamental improvements to React.

As this work is getting closer to completion, we're starting to think of what the next major releases of React DOM should look like. There are quite a few known problems, and some of them are hard or impossible to fix without bigger internal changes.

We want to undo past mistakes that caused countless follow-up fixes and created much technical debt. We also want to remove some of the abstraction in the event system which has been virtually untouched since the first days of React, and is a source of much complexity and bundle size.

We're calling this effort "React Fire".

🔥 React Fire

React Fire is an effort to modernize React DOM. Our goal is to make React better aligned with how the DOM works, revisit some controversial past decisions that led to problems, and make React smaller and faster.

We want to ship this set of changes in a future React major release because some of them will unfortunately be breaking. Nevertheless, we think they're worth it. And we have more than 50 thousands components at Facebook to keep us honest about our migration strategy. We can't afford to rewrite product code except a few targeted fixes or automated codemods.

Strategy

There are a few different things that make up our current plan. We might add or remove something but here's the thinking so far:

  • Stop reflecting input values in the value attribute (https://github.com/facebook/react/issues/11896). This was originally added in React 15.2.0 via https://github.com/facebook/react/pull/6406. It was very commonly requested because people's conceptual model of the DOM is that the value they see in the DOM inspector should match the value JSX attribute. But that's not how the DOM works. When you type into a field, the browser doesn't update the value attribute. React shouldn't do it either. It turned out that this change, while probably helpful for some code relying on CSS selectors, caused a cascade of bugs — some of them still unfixed to this day. Some of the fallout from this change includes: https://github.com/facebook/react/issues/7179, https://github.com/facebook/react/issues/8395, https://github.com/facebook/react/issues/7328, https://github.com/facebook/react/issues/7233, https://github.com/facebook/react/issues/11881, https://github.com/facebook/react/issues/7253, https://github.com/facebook/react/pull/9584, https://github.com/facebook/react/pull/9806, https://github.com/facebook/react/pull/9714, https://github.com/facebook/react/pull/11534, https://github.com/facebook/react/pull/11746, https://github.com/facebook/react/pull/12925. At this point it's clearly not worth it to keep fighting the browser, and we should revert it. The positive part of this journey is that thanks to tireless work from our DOM contributors (@nhunzaker, @aweary, @jquense, and @philipp-spiess) we now have detailed DOM test fixtures that will help us avoid regressions.

  • Attach events at the React root rather than the document (https://github.com/facebook/react/issues/2043). Attaching event handlers to the document becomes an issue when embedding React apps into larger systems. The Atom editor was one of the first cases that bumped into this. Any big website also eventually develops very complex edge cases related to stopPropagation interacting with non-React code or across React roots (https://github.com/facebook/react/issues/8693, https://github.com/facebook/react/pull/8117, https://github.com/facebook/react/issues/12518). We will also want to attach events eagerly to every root so that we can do less runtime checks during updates.

  • Migrate from onChange to onInput and don’t polyfill it for uncontrolled components (https://github.com/facebook/react/issues/9657). See the linked issue for a detailed plan. It has been confusing that React uses a different event name for what's known as input event in the DOM. While we generally avoid making big changes like this without significant benefit, in this case we also want to change the behavior to remove some complexity that's only necessary for edge cases like mutating controlled inputs. So it makes sense to do these two changes together, and use that as an opportunity to make onInput and onChange work exactly how the DOM events do for uncontrolled components.

  • Drastically simplify the event system (https://github.com/facebook/react/issues/4751). The current event system has barely changed since its initial implementation in 2013. It is reused across React DOM and React Native, so it is unnecessarily abstract. Many of the polyfills it provides are unnecessary for modern browsers, and some of them create more issues than they solve. It also accounts for a significant portion of the React DOM bundle size. We don't have a very specific plan here, but we will probably fork the event system completely, and then see how minimal we can make it if we stick closer to what the DOM gives us. It's plausible that we'll get rid of synthetic events altogether. We should stop bubbling events like media events which don’t bubble in the DOM and don’t have a good reason to bubble. We want to retain some React-specific capabilities like bubbling through portals, but we will attempt to do this via simpler means (e.g. re-dispatching the event). Passive events will likely be a part of this.

  • classNameclass (https://github.com/facebook/react/issues/4331, see also https://github.com/facebook/react/issues/13525#issuecomment-417818906 below). This has been proposed countless times. We're already allowing passing class down to the DOM node in React 16. The confusion this is creating is not worth the syntax limitations it's trying to protect against. We wouldn't do this change by itself, but combined with everything else above it makes sense. Note we can’t just allow both without warnings because this makes it very difficult for a component ecosystem to handle. Each component would need to learn to handle both correctly, and there is a risk of them conflicting. Since many components process className (for example by appending to it), it’s too error-prone.

Tradeoffs

  • We can't make some of these changes if we aim to keep exposing the current private React event system APIs for projects like React Native Web. However, React Native Web will need a different strategy regardless because React Fabric will likely move more of the responder system to the native side.

  • We may need to drop compatibility with some older browsers, and/or require more standalone polyfills for them. We still care about supporting IE11 but it's possible that we will not attempt to smooth over some of the existing browser differences — which is the stance taken by many modern UI libraries.

Rollout Plan

At this stage, the project is very exploratory. We don't know for sure if all of the above things will pan out. Because the changes are significant, we will need to dogfood them at Facebook, and try them out in a gradual fashion. This means we'll introduce a feature flag, fork some of the code, and keep it enabled at Facebook for a small group of people. The open source 16.x releases will keep the old behavior, but on master you will be able to run it with the feature flag on.

I plan to work on the project myself for the most part, but I would very much appreciate more discussion and contributions from @nhunzaker, @aweary, @jquense, and @philipp-spiess who have been stellar collaborators and have largely steered React DOM while we were working on Fiber. If there's some area you're particularly interested in, please let me know and we'll work it out.

There are likely things that I missed in this plan. I'm very open to feedback, and I hope this writeup is helpful.

DOM React Core Team Big Picture

Most helpful comment

I love every of these points, except the className change. It seems downright contradictory to the goal the other points are pursuing (aligning with the DOM API). React binds to DOM properties, not HTML attributes (this is this even articulated in the first point). The DOM Element property is named className, not class. So why would it be named class in React?

All 227 comments

I love this. Reducing bundle size and the "class" prop are changes that will be very welcome.

Great work!

🙂

Attention form library authors! 🤣

Great!

className → class is fantastic

What about all the others? Seems weird to still be doing clipPath, htmlFor, tabIndex, etc.

Adopting class is a major breakthrough in making the library more friendly for beginners. Congratulations.

This is awesome. I'm so curious how the move to class is actually going work with props.

Seems like ({ class }) => <div class={class} /> would initially present a reserved keyword problem?

This is fantastic news, thanks @gaearon!

I love every of these points, except the className change. It seems downright contradictory to the goal the other points are pursuing (aligning with the DOM API). React binds to DOM properties, not HTML attributes (this is this even articulated in the first point). The DOM Element property is named className, not class. So why would it be named class in React?

Fantastic! Do you have a goal for bundle size reduction?

👏

What about all the others? Seems weird to still be doing clipPath, htmlFor, tabIndex, etc.

I’m open to discussion but I’d argue these changes aren’t worth it (except for maybe).

I think a re-write of the event system is the most interesting aspect of this. There is significant opportunity to reduce the bundle size and ease community contributions.

Let's do it!

I wonder if it would be worthwhile to also work on releasing JSX 2.0 at the same time? People are going to need to re-learn a handful of things anyway. Maybe it's better to have to re-learn a bunch at one time rather than a few things twice over a period of time? Just a thought that occurred as I read this.

I love every of these points, except the className change. It seems downright contradictory to the goal the other points are pursuing (aligning with the DOM API). React binds to DOM properties, not HTML attributes (this is this even articulated in the first point).

And yet if we pass an unknown key/value pair it will be treated as an attribute since React 16. So we’re already inconsistent. Also, my comment was about users being wrong in expecting React to set value attribute. Whether React API uses attribute name or property name in its API is compeletely orthogonal.

I’ve defended your side of this argument for years but I think now that this is friction that’s just not worth it. You don’t gain anything from it. Just letting people use class has no negative effects except it doesn’t work with destructuring, and the migration cost. Everybody complains about it when they learn React. I think doing what people expect in this case is more important than being pedantic. And since we’re changing other DOM things anyway, let’s batch this together.

As long as React Fire is blazing fast.... 👍

These changes are all fantastic. I'm way excited about this and its implications for react-testing-library. In particular, events being bound to the react root (or maybe even drop event delegation altogether as it may not be necessary in modern environments anymore?), potentially removing/rewriting synthetic events, and onChange -> onInput will seriously improve the implementation of react-testing-library and the experience in using the tool.

I'd love to provide feedback on this as it's being implemented.

maybe even drop event delegation altogether as it may not be necessary in modern environments anymore

We considered this but think this might be an oversimplification. Event delegation lets us avoid setting up a bunch of listeners for every node on the initial render. And swapping them on updates. Those aspects shouldn’t be ignored. There is likely more benchmarking to be done there though.

@tannerlinsley ({ class: className }) => <div class={className} /> unfortunately this will kill jsx 2.0 object short hand notation (<div class />), but so be it ...

It would be super, super nice if class could finally take objects and arrays btw next to strings.

Do you have a goal for bundle size reduction?

Dropping a third of React DOM would be nice. We’ll see. It’s hard to say early but we’ll do our best.

Wow, this is an enumeration of almost all the design decisions I mention when people ask me about React cons.

Love the direction this is going.

What would the upgrade path be for libraries that use className and want to support multiple versions of React?

@gaearon Maybe it's not a big deal, today its nice to say "props are DOM properties not HTML attributes", now it'll be "except className, that one is the HTML name". I'd also like to be able to copy/paste SVG w/o 10 minutes of messing around with attributes to match up with React's

What about htmlFor?

It feels like the className -> class transition will have to be done very carefully, probably over an extended period of time. It's going to cause a lot of issues for the ecosystem, as virtually every component will need to be changed - even very trivial ones. This is mostly fine if you "own" the code and there's a codemod, but when you're dependent on 3rd party libraries you're largely at the mercy of maintainers.

The rest of the changes seem relatively low risk from an ecosystem point of view, but getting rid of className will really cause a lot of pain. It seems like it should be possible to split that into a separate issue and release it on a different schedule to the rest of the 🔥 changes.

I agree with @felixfbecker
Everything sounds awesome and would improve quality for both developers and users, but the className change.

Beeing able to deconstruct all properties but one would certainly cause more confusion and be harder to explain to new users than that they need to use className because class is a keyword (which they can clearly see when the misstake is made, as it's colored differently). Working around class in deconstructing requires introducing new syntax or a completely different way to read out that specific property that would only work untill you need to use a rest property.
It creates many problems just to save four characters.

@felixfbecker it could it be class/for in JSX and className/htmlFor in the JS version?

<label class="foo" for="bar">..</label>

would be

React.createElement('label', {className: 'foo', htmlFor: 'bar'}, '..')

Great plan! Nice to here that! 👏👏👏

This is awesome, I can't wait to dig into the new stuff.

Drastically simplify the event system (#4751).

Now react can not delegate handler to custom elements, without extending ReactDOM definition. https://github.com/facebook/react/issues/9242

It means React can not set custom handler on custom element like <x-component onMyEvent={ev => {...}} />

@gaearon
Do you have any plan about this?

All these changes are excellent but the biggest, meta change of them all is the size reduction in my opinion. React has a mean DX, but it's fat enough that downloading and parsing it on average networks and in particular mobile is a problem.

We could have it all!

Would rewriting the event delegation open the door for fixing #1254; where some events degrade perf for the node they are attached to (which for React means the whole app).

Also, as a long shot, have you considered having synthetic dataSet props? Not being able to type allowable HTML props (because of data-*) leads to a ton of bugs when forwarding props to the DOM nodes. Currently typed React components have to choose between exact types for props and allowing data attributes.

I'm excited

@ryanflorence A bit offtop but it's kinda sad that no one (as far as I know of) had the idea to make a html/css/svg -> jsx transfomer in order to ease migrations to React with so many trivial changes to map HTML attrs to React props. So many man-hours wasted perfoming mostly find-and-replace :(

It's very strange to see the following in the same issue (and the comments):

Our goal is to make React better aligned with how the DOM works
className → class
Seems weird to still be doing clipPath, htmlFor, tabIndex, etc.

When all these are direct counterparts of DOM APIs

And this argument doesn't pass muster:

I’ve defended your side of this argument for years but I think now that this is friction that’s just not worth it. You don’t gain anything from it. Just letting people use class has no negative effects except it

React has always been just a very thin layer on top of JS. So everything else besides JSX's angle brackets was JS _and_ had a direct counterpart in DOM APIs for things directly related to DOM: className, clipPath, htmlFor, tabIndex etc. etc. etc.

With the decision to introduce class React stops being a thin layer (class is a reserved word in JS) and breaks away from the declared goal of being more compatible with DOM APIs.

It's especially jarring to see that you want to both "Migrate from onChange to onInput" because it's inconsistent with the DOM _and_ become inconsistent with the DOM by migrating className -> class.

Additionally, this opens a full can of worms, as people will demand (and are already demanding) changes to other parts. Just in the comments: why do we use dataset instead of data-*? Maybe because data-* is not valid JS (much like class) and because it's consistent with the DOM APIs? Why don't we change htmlFor? Because for is a reserved word and htmlFor is in DOM APIs? Etc. etc. etc.

And yet if we pass an unknown key/value pair it will be treated as an attribute since React 16. So we’re already inconsistent.

@gaearon which is also the reason why React is the only library scoring bad on the CustomElements Everywhere interop test: https://custom-elements-everywhere.com/
And there's many issues asking to change it: https://github.com/facebook/react/issues/11347, https://github.com/facebook/react/issues/7249, https://github.com/facebook/react/issues/9230

This may be a non-issue, but is the similarity of React Fire to React Fiber going to be confusing for non-native English speakers? I've often thought that Newark Penn Station and New York Penn Station being on the same train line here in NYC was a particularly cruel joke on tourists.

If it's a real concern, you could call it React Flame and still keep the fire 🔥 emoji.

Would rewriting the event delegation open the door for fixing #1254; where some events degrade perf for the node they are attached to (which for React means the whole app).

Fixing #1254 in some form is definitely something I’d like to see.

Also, as a long shot, have you considered having synthetic dataSet props?

I don’t want to commit to something like this now because there’s a bigger surface. Richer interface for DOM (ariaSet, dataSet, classList) makes sense but it’s not clear how much we want to invest in that in React DOM, versus a higher level library that gives you a richer DOM API. Since these changes are more cosmetic but have high surface area, I’d hold off with them.

React Blazing 🔥

@ryanflorence A bit offtop but it's kinda sad that no one (as far as I know of) had the idea to make a html/css/svg -> jsx transfomer in order to ease migrations to React with so many trivial changes to map HTML attrs to React props. So many man-hours wasted perfoming mostly find-and-replace :(

@jxub
https://transform.now.sh/html-to-jsx/

So excited for the new changes, you humans from Facebook are making history with this migration. 50k components will be migrated over to React Fire ?
Your test suites must be so tight <3

is the similarity of React Fire to React Fiber going to be confusing for non-native English speakers?

maybe for hard-of-hearing folks, too (or those with other conditions affecting word discrimination)

Thanks for sharing @gaearon, that’s amasing!

I’d love to see a JSX 2.0 release solving the white space and new lines issue that occurs when we compile our code with Babel and Typescript.

There are different issues opened and I tried to contribute but got told off cause the React team needs to review everything around JSX.

This issue relates to the dom anyway, cause the way we translate jsx to js does not allow to what the w3c says.

This is the issue https://github.com/facebook/jsx/issues/19

My comment is at the very end.

I think, className is ok. Let it be what it is. Don't add insult to injury.

Can someone please clarify how this affects existing handlers?

Stop reflecting input values in the value attribute

Will React still have controlled inputs with accurate event.target.value updates in handlers, or does this only affect reading values from refs and DOM Nodes?

@nickmccurdy it affects what you see in the browser devtools

@tomdale React Ember 🔥

Nice! I'm waiting to see full list of changes in React 17.
I believe it will be a new era of ReactJS. 🔥⚛️

@tomdale Hmm: Yarn, Fiber, Fabric; maybe another clothing related term could be used? 😆

And yet if we pass an unknown key/value pair it will be treated as an attribute since React 16. So we’re already inconsistent.

@gaearon which is also the reason why React is the only library scoring bad on the CustomElements Everywhere interop test: https://custom-elements-everywhere.com/

No, that's not the reason (custom and normal elements are completely separate code paths). The reason is that we already had the old behavior and didn't want to break backwards compat unless the new behavior was solid. I think it would make sense to tackle better custom element interop as part of this umbrella.

is the similarity of React Fire to React Fiber going to be confusing for non-native English speakers?

Both are internal codenames and don’t really carry any significance once the projects are completed. React Fire is just an effort to make React DOM better — and by the time it becomes production-ready it will be just React DOM.

Will React still have controlled inputs with accurate event.target.value updates in handlers, or does this only affect reading values from refs and DOM Nodes?

Yes, because event.target.value is a property. We're talking about stopping updating attributes. Which no other popular libraries do (AFAIK) and which causes countless problems. It shouldn't affect your code unless you're relying on CSS selectors on the value (which is probably bad anyway).

Nice! I'm waiting to see full list of changes in React 17.

Note we're not committing to this being ready by 17. It might be 18. Or 19. 😅

It's nice to see steady development on such a good library like React. 🎉 class will make the usability much better, it's worth it imo

@gaearon

custom and normal elements are completely separate code paths

That itself seems like something to fix too. Is there any reason not to treat all elements the same? That's the intention of the HTML and DOM specs.

@justinfagnani As discussed previously, the reason we didn't do it at the time was because there was no convention for how to tell whether to set a property or an attribute — and there was a risk that by using a check we risk making it impossible for web platform to add new properties to the prototype. I think by now there's already some sort of consensus in the RFCs and PRs that @robdodson has been working on, and we can probably pick it up from there.

👍 🔥 🔥 🔥

React Fire should also allow us to apply some of the clever performance optimizations that Inferno has – but haven't been able to apply because of breaking changes. Exciting times :)

LGTM

Related to the proposed className -> class rename: I'd love a classes property that took an array of strings. That would save me the trouble of a lot of string manipulation (or the use of classnames) in my components.

I think the only disadvantage of a classes prop would be that arrays containing the same strings in the same order would cause a re-render in pure component, while a string of the same CSS classes wouldn't. Honestly though, it seems like a minor issue. I think most React devs already know the tradeoffs of arrays & objects in props.

@gaearon have plans for backward compatibility? Maybe follow the same path as React Fiber, adding warnings about the changes and giving time for large codebases update that without losing new updates.

Regarding class and className.

I know we won't get a broad agreement on this whichever way we go. People have really strong opinions about this one. I want to share how I'm thinking about it, in a hope that it will be helpful.

Component API Should Feel Idiomatic

There is a common argument that React "matches JavaScript" and thus className is preferred. I think this assertion is subtly misunderstood so I'd like to focus on it a little.

In React, first and foremost, we care that using a React component should feel like idiomatic JavaScript. This means that if I use a hypothetical <Table> component, I expect its props to be camelCase:

<Table
  rowHeight={10}
  headerBorderInset={5}
  renderRow={this.renderRow}
/>

I don't expect to see prop names like row_height or row-height in a component's public API. Component's props are an object (kind of like an "option bag"), and we generally expect those options to be camelCase. This may not be always idiomatic in DOM, but the DOM is not very consistent in many places. React aligns with the JavaScript ecosystem which overwhelmingly uses camelCase.

But what about the DOM? This is where it gets thorny.

DOM Properties Are Not Just "Attributes in JS"

In DOM, we have attributes and properties. Attributes are the things you usually see in HTML. Properties are the things you usually set from JS. But crucially, DOM APIs exist both for setting properties and for setting attributes — and they're not even always setting the same thing.

node.value = 10; // setting a property
node.setAttribute('value', '10'); // setting an attribute

In many cases it doesn't matter. In some cases it does. But maybe not in the way one might think from using React (which has one abstraction over both).

React Is Not Just Setting Properties

A common misconception is that since React currently uses the camelCase convention for most DOM props, it means React is setting DOM properties. This is wrong.

In fact, React is currently using attributes for almost all props it supports. In some cases, like value, this is causing issues (which as I discussed we want to revert). In other cases, this is actually great — because we don't have to include a list of supported properties into the React bundle. Using attributes under the hood is what allowed a major size reduction in React 16.

My point here is that whether React uses properties or attributes internally is an implementation detail — and has nothing to do with whether React DOM element _API_ should be using property names, attribute names, or even some other names that make sense.

Still, Let's Just Use Property Names?

Okay, properties and attributes are an implementation detail. But why not just standardize on using DOM property names since those were specifically made "for JavaScript"? Isn't that how React API is designed today?

Well, not quite. Only one of the props enumerated below corresponds to a real DOM object property:

<div
  tabIndex={1}
  data-id="123"
  aria-live="polite"
  nopin="nopin"
  itemType="http://schema.org/Movie"
  onClick={function() { alert('hi') }}
/>

Ironically, the only prop above that has an actual DOM property with the same name corresponding to it (tabIndex if you weren't sure) is actually being set by React as an attribute!

So by this point you probably see it's neither clear-cut nor consistent. In some cases properties don't exist (such as for custom, non-standard attributes), in some cases React could provide a richer API (data- vs dataSet) but currently doesn't.

In some cases React intentionally chooses to deviate (onClick in React vs onclick DOM property) because it makes more sense for custom React components. This is because React components often expose more complex event handlers like onItemClick. It would be very inconsistent if you wrote <Button onclick> but <Table onItemClick>. And <Table onitemclick> isn't camelCase, which we wanted to avoid in a component API.

Above, I explained that React already isn't consistent about "always using DOM property name", that React doesn't even actually use properties internally (so that rule of thumb doesn't describe the actual mechanics either), and that in many cases DOM properties simply don't exist so we have to stick with allowing the attribute name.

If Not Property Names, Let's Be Consistent and Use Attribute Names?

So why not go with using only attribute names? This could be plausible. But now we bump into the very first consideration we brought up. Using a React component should feel like idiomatic JavaScript. But often components forward at least some props to the underlying DOM element.

<Button
  borderColor='red'
  tabIndex={1}
 />

 // renders...

 <button
   tabIndex={1}
/>

It would be awkward for a custom Button to accept props with inconsistent capitalization:

<Button
  borderColor='red'
  tabindex={1}
 />

This forces the consumer to always remember if a certain prop is an actual DOM prop, or just a part of the component contract. Even that distinction is fuzzy — a component may choose to first pass a certain prop through, but then to actually start using it for some extra logic. Where do you put the boundary between "DOM props" and "other props"?

I think this is the primary reason it's desirable for props like tabIndex, cellSpacing, and most other DOM-related props to follow the camelCase convention. It's not because they're DOM property names. It's because they often end up in component APIs. And we want component APIs to be consistently camelCase.

We want to make it easy for custom components like Button to wrap and forward them without either "translating" them to the attribute name at the point where they flow into the DOM, and without introducing non-camelCase props into a custom component API.

This also explains why props like data-*, aria-*, and custom attributes are reasonable exceptions (even though we could make richer APIs for them). They are rarely passed to custom components. Typically, they are too coupled to the DOM to be useful in custom components — and instead, they become an implementation detail of something like a <Modal> or a <Button> with a richer camelCase API.

React Properties Already Don't Match DOM Properties

If the "DOM property name" convention didn't work out, we need something else. What is it? Could it be "camelCase version of the attribute name"? It seems like this almost always already checks out.

If this sounds too radical, consider that we're already doing this. We support something called srcSet. But the DOM property name for it is srcset. We have autoCapitalize but the DOM property is called autocapitalize. We have autoFocus but the DOM property is autofocus.

We are already deviating from DOM property names when they don't match the camelCase JavaScript convention. Which brings us to class.

Finally: className vs class

Part of the original justification for making it className was because React was setting DOM properties, and className is the name of the DOM property.

However, as I explained above, React doesn't use properties anymore except three or four special case. More importantly, React doesn't even consistently use DOM property names — rather, it uses names that would look natural when used from JavaScript, regardless of internal naming inconsistency in either DOM attribute and property names. And that is because React cares most about keeping prop names for custom components feel "JavaScripty". In this sense, tabindex is not "JavaScripty" — but both class and className are.

Another argument against class early on was that code like this wasn't valid ES3 (relevant for IE8):

// Not valid in ES3
// Valid in ES5
var props = { class: 'foo' };

But most don't write ES3 anymore. Either you're using a toolchain like Babel or you're likely targeting IE9+ — React doesn't even support IE8 now.

So the only inconvenience left with class is that you can't write this:

// Not valid at all :-(
const class = props.class;
const { class } = props;

But I think that with time, this argument is not strong enough by itself. React doesn't force you to use destructuring or using this specific variable name, and writing something like

// Valid
const {class: cls} = foo;
const cls = props.class;

isn't that much more effort.

Typically people pass class down far more often than read it because many components contain more than one inner <div> or other host elements. So you end up writing <div className> much more often than wanting to destructure class. And thus the change to class would save more keyboard typing than it would introduce.

There is another important point here.

Passing class down through many levels is not a great pattern by itself. It's necessary for libraries, but in application code it often leads to fragile components. The ones whose styles break all the time because there's a hundred different callsites each appending a different class, causing cascade bugs. So it's not clear how valuable it is to encourage destructuring class in the first place. I think it's fine that you need to write one more line of code to read it from props (or you can just use props.class and not think about it).

If you are writing a component that is very close to the DOM (and thus it makes sense for it to take class as a prop), you likely want to forward other props too. So you can use rest syntax in destructuring:

// Valid in ES2018

function Button({ color, ...rest }) {
  const buttonClass = rest.class +  ' Button-' + color;
  return <button {...rest} class={buttonClass} />
}

And if you didn't need to modify it then you could've just forwarded {...rest} without even reading class from it. So the destructuring limitation might help encourage better component design.

Why Not Both?

why not both

Finally, can't we just support both class and className? In a way, we already do, but React yells at you for it with a warning. There is a reason for this.

If we support both without warnings, then the community will split over which one to use. Each component on npm that accepts a class prop will have to remember to forward both. If even one component in the middle doesn't play along and implements only one prop, the class gets lost — or you risk ending up with class and className at the bottom "disagreeing" with each other, with no way for React to resolve that conflict. So we think that would be worse than status quo, and want to avoid this.

Summary

If React was open sourced today, it seems like the pros of allowing class (closer conceptually to what most people expect, less typing for the most commonly used prop) outweigh the downsides (slightly more typing to intercept it — in which cases you'll probably just want the spread operator anyway).

What we used to see as downsides (inconsistent with DOM properties) is moot because we neither set DOM properties anymore, nor even strive to be consistent with them. Instead we're aiming to have an attribute-based but camelCase API on the consuming side of React components — which we're already almost consistent at. And class="Button" is clearly more convenient than className="Button". In fact if DOM API was designed today it would probably let you set .class property precisely because the restriction on using class in an assignment like this was lifted in ES5 — almost ten years ago.

The only remaining big downside is the migration cost. We'll assess this carefully. But if we're doing a bunch of changes anyway, we might be able to make this one too and fix it for good. We're not thinking about this lightly and we take all your concerns into consideration.

Note: this might make sense to do for other React prop names that don't match camelCased attribute names. I'm thinking of htmlFor.

@renatoagds

have plans for backward compatibility? Maybe follow the same path as React Fiber, adding warnings about the changes and giving time for large codebases update.

As I noted:

And we have more than 50 thousands components at Facebook to keep us honest about our migration strategy. We can't afford to rewrite product code except a few targeted fixes or automated codemods.

So we'll definitely try to make migration strategy as smooth as possible, just like we always do. If it's not smooth we won't be able to make the change ourselves.

re: className -> class, I'm cool with whichever decision, I can definitely see the exception to changing class for new users, and a side benefit of shorter lines of code. Although, they would still need to learn about the other camelCase names.

So the only inconvenience left with class is that you can't write this:

const { class } = props;

But I think that with time, this argument is not strong enough by itself. React doesn't force you to use > destructuring, and writing

const class = props.class;

isn't that much more effort.

Two (possibly small) things:

  1. Isn't const class = props.class invalid JavaScript? I didn't think that it was, and in a quick test Chrome doesn't like it. Also, this article suggests that it is not valid.

  2. I could see this change being a (once again, potentially small) area of frustration for folks that write components like this: nvm (see Update below)

const { oneProp, twoProp, className, ...rest }  = this.props;

// do stuff with oneProp, twoProp, className

return (
  <div
    someProp={prop}
    anotherProp={anotherProp}
    className={someClassName}
    {...rest}/>
);

After this change, this would need to be something like...

const { oneProp, twoProp, ...rest }  = this.props;

// do stuff with oneProp, twoProp, this.props.className

return (
  <div
    someProp={prop}
    anotherProp={anotherProp}
    {...rest}
    class={someClassName}/>
);

It's not impossible to work around this change, but it is a little bit more to keep in mind when both writing and reading components in this style.

Update:

Nevermind,

const { class: className } = this.props;

does the trick.

The only remaining big downside is the migration cost. We'll assess this carefully. But if we're doing a bunch of changes anyway, we might be able to make this one too and fix it for good. We're not thinking about this lightly and we take all your concerns into consideration.

Luckily this is easily mitigated if one is using a CSS-in-JS approach, like Aesthetic. Thanks for the amazing write up!

Random tip regarding attribute names, I found the excellent project, svg2jsx is great for converting large SVGs to use in React!

@jamesplease Sorry, my mind blanked — you're right. Edited.

@jamesplease you are right. That also comes up frequently working with JSON, for the default value, so annoying!

const { default: defaultVal } = property

While you're changing the event system, it would be really nice to see something similar to Inferno's linkEvent function so we can do event handling using props in functional components without having to create an anonymous function each render.

className -> class will be a huge change for the ecosystem, numerous unmaintained components will become incompatible, and there will be nothing you can do if you cannot patch them. Maybe have some wrapper like StrictMode that disables this change for the components deeper in the tree to provide a gradual migration path?

I could see this change being a (once again, potentially small) area of frustration for folks that write components like this:

const { oneProp, twoProp, className, ...rest }  = this.props;

// do stuff with oneProp, twoProp, className

return (
 <div className={someClassName} {...rest}/>
);

Just don't destructure it.

const { oneProp, twoProp, ...rest }  = this.props;

return (
  <div
    {...rest}
    class={'something ' + rest.class}
  />
);

@gaearon

whether React uses properties or attributes internally is an implementation detail

That seems like a problem too, honestly. DOM elements can and do behave differently in some cases depending on whether you're setting attributes or properties. React can't possibly know about all of the differences, but users of elements can know about the elements that they use. Explicit control over properties, attributes and events would let authors break out of this situation.

@justinfagnani If you have specific things you'd like us to change, do you mind filing a separate issue with an API you suggest? This sounds a bit out of scope of this issue.

@sompylasar

className -> class will be a huge change for the ecosystem, numerous unmaintained components will become incompatible, and there will be nothing you can do if you cannot patch them. Maybe have some wrapper like StrictMode that disables this change for the components deeper in the tree to provide a gradual migration path?

I agree — and we're still weighing pros and cons. Nothing is finalized.

But in practice, the problem of unmaintained components is not new — it comes up every React major release because something changes in majors by defintion (we can't move forward otherwise, and we don't have the luxury of keeping all legacy code in the bundle forever unlike in e.g. server environments). The same problem came up when PropTypes moved into a separate package, and will happen again with the UNSAFE_ lifecycle renaming. Unmaintained packages get forked or patched. We realize it's a big time sink, and this is why we avoid doing more than one big major in a year. For folks who can't afford to help, typically waiting a few months before moving to a new major is the best strategy because early adopters pave the way and revive the abandoned packages. Then we move forward all together.

Again — I very much understand your hesitation, but this is not principally different from other breaking changes that happened in the past, or that might happen in the future. As always, we'll put a lot of effort and emphasis on automated scripts you can run to convert most of your codebase, and that you can run on other packages as well (and send PRs to them — or fork them in their last state).

Regardless of your decision, one more argument against class is searchability. How many false positives will give you searching by class when you wanted to find components that use CSS classes in ES6 code that uses JS class components? Yes, you can search for class={, but what about destructuring of props in JSX that are created as an object in JS? (I'm against heavy use of props destructuring but they are still used) Of course, we need better context-aware AST-based search tools in code editors, but for now we only have text and regexp. Of course type systems may help to track object passing, but large population hasn't adopted them.

Something about using a reserved word just doesn't sit right with me, even if it doesn't cause any issues now; can we say for sure that rest.class (for example) won't be significant to the language in x years?

@GeordieP If it works today it can't break tomorrow. That's the core principle of how JavaScript is being evolved, and the reason for many its idiosyncrasies.

@gaearon Fair enough, then. If it's a big enough win, I say go for it.

@sompylasar I usually search for className= or className:, seems like both of these would work with class too.

Please get rid of className, and for god's sake, htmlFor. I am not a DOM developer, usually there is something very very wrong if I have to access native DOM methods. The biggest challenge I have onboarding people to React is the abstraction JSX makes over the DOM and its weird replacement HTML attributes. Everything is being transpiled, no reason to worry about reserved words at this point. IMO.

Not sure this is adding anything to the existing discussion, but it seems like there should be a better reason to change className.

Is saving beginner React learners from a slightly unintuitive name worthy of everyone having to update their projects & behavior?

As someone who uses de-structuring liberally, having to remember this new exception to the rule is probably a larger mental hiccup than the rare occasion that I write class instead of className.

Also, wouldn't beginners still be confused by the vast amount of material (as blogs/repos/etc) that uses className currently?

Finally, as @sompylasar said, this hurts search-ability within my codebase.

Maybe this is a tabs vs spaces type argument, but I don't totally understand why this change is necessary. It seems like a large cost for little gain unless this is part of a larger shift in how you want to model the API over time. That said, I'll for sure be using whatever ya'll decide 😅.

A bit offtop but it's kinda sad that no one (as far as I know of) had the idea to make a html/css/svg -> jsx transfomer in order to ease migrations to React with so many trivial changes to map HTML attrs to React props.

@jxub - I built a HTML to JSX converter as part of a hackathon way back in 2014: https://magic.reactjs.net/htmltojsx.htm. I'm not sure if it handles SVG well, though. The hackathon project was to make a script that would "ajaxify" a plain HTML site by using React (https://github.com/reactjs/react-magic) and part of that required me to build a way to create a React component from a chunk of HTML, so I released the HTML to JSX part as a separate standalone page.

We still care about supporting IE11 but it's possible that we will not attempt to smooth over some of the existing browser differences — which is the stance taken by many modern UI libraries.

@gaearon - What's some examples of modern UI libraries that do not smooth over browser differences? For me, that's one of the major reasons to use a library.

Conspiracy theory: this entire className / class news is one bright controversial thing that everybody will immediately pay attention to and argue about. It's either to attract attention to the rework project as a whole, or distract from something bigger that is happening in the shadows, or give one thing that can be later retracted while the rest will be accepted, like in the following anecdote:

The great theatrical artist Tyshler, creating sketches of scenery, in the corner drawing a small green dog. And when one of the admissions committee asked: "I like everything, but where is this dog?", The artist with a sigh of regret plastered her.

The true reasons behind this change aren't clear, but they have already skyrocketed the popularity of this new upgrade project and the community buzz around React.

It would be nice if supporting Passive Event Listeners were within the scope of React Fire, which is an important feature on mobile.

6436

Thanks for all your hard work on this, but please reconsider className -> class.

We were all once React newbies and className didn't prevent us from learning and loving React.

I remember when I'm using vue with jsx, they already had class not className, I disagree if className will be changed to class, because React is pioneer in Virtual DOM, and represent of DOM it self.

Attach events at the React root rather than the document

@gaearon Does this mean that in a testing environment I won't have to append elements to the document to be able to dispatch real browser events and have the handlers associated to them be called? Having to do this is very counter-intuitive and I'm sure has been the source of many developers unfamiliar with React's internals get confused, waste a lot of time writing tests and incurring in event simulation and poor testing practices.

Which takes me to another note, can we do something about react-dom/test-utils ? I'm specially interested in the possible removal of Simulate given all the issues associated to it that we all know, and of course do the necessary changes in react-dom itself so that it is truly not needed anymore. Could that be in scope?

/cc @kentcdodds

I love the direction and big picture view that React Fire is taking. So far those look like great changes to work towards.

Love most announced changes, but I'm sceptical about the className change.

React doesn't force you to use destructuring or using this specific variable name, and writing something like (...code snippet...) isn't that much more effort.

While it isn't much effort to write, in my current experience I'd expect it to be _way_ harder to explain to other developers (especially developers from other languages). On the other hand in all the years we used React at our company I guess only one or two devs were confused by className and just accepted this as Reacts API for setting the class names within a couple of minutes.

(In my personal opinion while I _love_ destructing, the renaming syntax sometimes feels weird in itself for beginners, because it is different than renaming in imports which looks quite similar and can be combined with things like default values. One _could_ just not use destructing then, but that would be a _big_ exception to all other code we currently write at our company. Experience from others May differ of course, but that is my view on the problem 🙃.)

great

Also sceptical about the className change. It's one of the most minor changes in the scheme of things, but it's attracting a massive chunk of the commenting discussion here.

Is it really worth spending that much political capital on a change, when there's so much other good stuff that's being announced?

From where I stand, if you're making a decision where part of the justification is "and writing something like... ...isn't _that_ much more effort.", that decision has got to have a _massive_ upside, and the className -> class change just doesn't in comparison to everything else that's been announced.

this will be a major advance on 🔥 React Fire

About class v/s className, I think we should remind ourselves that JSX ≠ React.

Since JSX is a _DSL_ that's designed to look like HTML, it's best to keep it as close to HTML as possible. Granted it's called className in the DOM API, but most are using JSX probably because they don't want to deal with DOM API directly.

If it makes more sense for React's API to closely match DOM API, then I hope it's ok/possible to do the mapping in the transpilation:
<img src="avatar.png" class="profile" />React.createElement("img", { src: "avatar.png", className: "profile" }).

It would be very valuable to make JSX syntax a clean superset of HTML.

To add to what @mhenr18 has said.

Current state of things:

  • React is inconsistent
  • API is camelCase

Proposed state of things:

  • React is inconsistent
  • API is camelCase
  • className -> class

Perceived benefits:

If React was open sourced today, it seems like the pros of allowing class (closer conceptually to what most people expect, less typing for the most commonly used prop) outweigh the downsides (slightly more typing to intercept it — in which cases you'll probably just want the spread operator anyway).

Actual downsides:

  • the entire ecosystem that depends on className stops working (huge upgrade effort)
  • the entire vast body of books, tutorials, examples, code, posts, articles becomes ever so slightly invalid
  • the only valid JS remaining is additional typing: const {class: cls} = props. Every other possible use-case in plain JS becomes invalid
  • React API remains inconsistent, and breaks further assumptions (why htmlFor not for etc.)

If I were a product manager, my immediate reaction to the change would be: wat?

@gaearon You might have considered this already, please tag PRs with "React Fire" or a similar keyword.

Issues usually are tagged correctly, PRs sometimes don't have them. This helps potential contributors.
This is coming from my experience when I was trying to read through the git history looking for React Fiber and React Reconciler related commits during the whole Fiber development. It helps those of us who are who are trying to figure out whats happening and see if we can contribute in some ways.

I also think renaming className to class will cause such a big migration effort and problems for new devs.
The className attribute is so visible and heavily used that it will literally break all libraries relying on react.
And this is not enough, most tutorials will be broken. A new device copy & pasting from an article will be wondering "why is that not working, it says className is not a valid prop".
So the senior has to help and we have not gained anything, because we still have to explain why it does not work like you would expect.
And for real, explaining that you have to use className to define classes on the component takes under a minute and is easily understandable. Explaining why they changed from className to class to every developer takes much longer and results in more frustration.

All the efforts required for a single word.
It will not change anything in the way react behaves.
It will not boost the development of react-dom.
It will not increase productivity of devs working longer than a week with react.
It will just break everything.

Please think about it, is it really worth it?

I have been using babel-plugin-react-html-attrs for years and it has been serving me well, I don't think renaming className to class is a good idea. It's better achieved by a plugin like the one I mentioned.

Wasn’t there a Babel plugin to handle the whole “class v className“ / “for v htmlFor“ situation?

I hope it's possible to support html attributes as-is while maintaining backward compatibility with the naming decisions already made.

The fact that there are babel plugins already to do the conversion, is perhaps evidence that it should be possible to support it in the core JSX transpiler itself. But making it an official spec would make things so much easier and reliable for everyone.

I'm not aware of React internals, so can't say much about the real feasibility. Only expressing how I think it "should be" in terms of user-friendliness.

Please reconsider className vs class. As it has been said above, the gain is almost inexistant but there are real downsides.

One being that for us using Haxe as a language to write react applications, this would not only be a breaking change but will simply prevent us from writing _any_ react application.

class is a reserved keyword in most programming languages, and we could simply not manipulate this prop anymore, making react application near impossible (good luck creating a real one without manipulating classes). Same goes for htmlFor vs for, sadly (this one is really ugly but I'm grateful it exists).

Oh by the way, searchability... Imagine you google for "React class", you'll get mixed signals: React class components, React class attribute. You google for "React className", you'll get outdated documentation (people mentioned above the huge amount of upgrade work geterated by this change besides code upgrades).

Is the goal of this project to generate more work for the community and more noise and mixed signals for the Internet? I hope not.

Yes, the Web and JavaScript ecosystem struggles to maintain back-compat with stupid mistakes of the past, but this strive for back-compat is what allowed it to grow up to such scale without major fragmentation.

I understand that there's no progress without change, and myself adopted the early FB's motto of breaking things to move fast (also having a plan how to re-assemble them back in advance).

If you're being so persistent that this change is really needed, just tell the true reason for the change. It can't be the "hard to learn" thing, sounds too shallow for the mighty React Core team. You definitely should have something in mind.

@sompylasar I usually search for className= or className:, seems like both of these would work with class too.

Also, good luck doing that directly on github =/

I've read all comments here. I'm fine with changes, but not sure about class. Main reason is because it will not work with plans for JSX 2.0. about shortcut notation (as we have now in objects).
Everything else seems to be very nice improvements.
Waiting for the final decision :) Thanks for your amazing efforts folks!

In one of your comments, you mentioned “if it works today, it should work tomorrow”. Keep this in mind before my ask ;).

I maintain a UI library called RMWC https://jamesmfriedman.github.io/rmwc/ that targets all versions of React back to 15.4. I’ve been able to maintain a unified api surface, but changing className to class, onChange to onInput, and overhauling the event system will make this nearly impossible to continue.

Is there any possibility of a “compat” layer that can translate some of this stuff under the hood? So I can write in whatever version react fire is but continue to run in a lower runtime.

If not, I expect my weird code is going to get weirder. 👀

if className really has to change to class, please also change htmlFor to for.
since class and for originally invalid when we destructuring props.

If React has its own API that normalises a lot of attribute names to make a consistent API then surely className is a perfect fit for that. One extra idiomatic learning on top of the camelCase idioms, plus the fact it maps on to the DOM property name (like some of the other JSX attributes) is surely fine in comparison to having to destructure it as a special case every single time you use a props object with it on.

I feel like, up to now, you have heard a lot of vocal opinions against the status quo (“why can’t we have class?!”) while everyone who was happy using it stayed quiet; in line with the old adage that people are much more likely to give negative reviews than positive ones. Personally I think a tiny learning curve on top of many others is far better than special casing this prop every time it appears.

I think I’d like to see a bit more democracy around it - I think this very specific change can be done by committee (it was essentially a pseudo committee that got you to consider this change) - and I’m happy to be on the losing side. Just so that it affects the least people day to day.

P.s. everything else looks great btw, especially the event stuff 👌🏻

waiting for new updtates

@gaearon Is it worth considering that your own example:

const { oneProp, twoProp, ...rest }  = this.props;

return (
  <div class={'something ' + rest.class} {...rest}/>
);

contains a bug and wouldn't work as you expect it to? It might not be as straightforward as "Just don't destructure it."

am I the only one who wasn't that concerned about class being className? If you were to ask me what problems react had that would never even cross my mind.

Still, updates are freebies and I love freebies.

am I the only one who wasn't that concerned about class being className?

I'm not at all worried about class. The subtle bugs with IE11 are going to be funnier (and I'm still more than fine with these changes nonetheless)

It would be nice if supporting Passive Event Listeners were within the scope of React Fire, which is an important feature on mobile. #6436

I agree. It’s definitely in scope of this work.

Hey @gaearon - first of all thanks.

Have you considered doing a survey in order to gather information about how people are using react and what APIs are being used and what's confusing?

While React is by far my favourite frontend development stack I find there are a few footguns when learning and first using it. I don't want to just lay out what people I taught found confusing personally.

I've found it to be useful in projects I've been involved in personally (Node, Bluebird etc).


In terms of what I'd personally like to see in core: I'd love better simpler non-global dependency injection support, better and more consistent event handling (which you're already discussing).

Have you considered doing a survey in order to gather information about how people are using react > and what APIs are being used and what's confusing?

I think react-native has a canny.io I didn't realize react doesn't

I maintain a UI library called RMWC https://jamesmfriedman.github.io/rmwc/ that targets all versions of React back to 15.4. I’ve been able to maintain a unified api surface, but changing className to class, onChange to onInput, and overhauling the event system will make this nearly impossible to continue.

Is there any possibility of a “compat” layer that can translate some of this stuff under the hood? So I can write in whatever version react fire is but continue to run in a lower runtime.

I empathize with your concerns (I also wrote my fair share of libraries). However, since there are so many tradeoffs with changing anything, we tend to err on the side of putting effort into helping people upgrade rather than helping people support multiple versions. It doesn’t mean we don’t do that at all (we did release a polyfill for library authors for lifecycle changes in 16.3) but that we don’t optimize for this use case.

My suggestion is cut a new major when we cut a new major if something is incompatible. People can keep using old major versions of your library if they want to. I know it’s more effort to maintain, especially if you keep developing it, but it’s questionable whether a single hacky codebase is better than two diverged but consistent codebases.

@jamesmfriedman it should be possible (and not too complicated) to update (automatically) with a codemod) to the new API and then (as part of the build process) transform (again with a codemod) to the old API. Then ship both bundles and require them dynamically based on the React version.

That is some work but it can be done once (as a webpack plugin or npm install script for example?) and shouldn't be hard. I'd assume it's the kind of thing to be released once and used in all libraries.

Like a compile time compatibility layer specifically for libraries.

I support dropping htmlFor for for.

Do you think a bridge release that supports both className and class would be a good idea? It would allow developers to use older, third party React code while their current first party code can be future facing. Once the third party updates, it's as easy as dropping the bridge package to have a fully optimized React DOM.

React API remains inconsistent, and breaks further assumptions (why htmlFor not for etc.)

I think you missed the last sentence when I said for should also change. Then I think we’ll be fully consistent with “camel cased attribute names” except one or two rare SVG attributes. Which we can change too.

Wasn’t there a Babel plugin to handle the whole “class v className“ / “for v htmlFor“ situation?

I see quite a few mentions of this so I’d like to clarify. A Babel plug-in isn’t a great place to do this conversion because it doesn’t work for cases like

const props = {
   class: "foo"
}
<div {...props} />

It also doesn’t make sense that you pass class but get className in props of the component. Or, if the plugin only targeted platform elements, it would be odd that replacing button with a Button changes what the class prop compiles to.

Using Babel plugin for this just obscures the semantics of the code you’re writing and leads to more confusion. In my view it is a worse solution than either allowing only className or class.

Fun fact: this is how a very old version of JSX compiler worked. This behavior was removed because of how confusing it is.

Awesome changes . . .

Amazing! Thank you all the contributors for working to bring such exciting updates.
I had no idea className > class was such a big scandal. Personally I've gotten used to it.

Reduced bundle size & upgraded event system sound really promising. Super stoked!

I'm really looking forward to separating the polyfills of the event system as separate packages.
But whether className > class is too radical, you know, React has too wide an impact.
Will this lead to the unforeseen of many systems ?

Dropping a third of React DOM would be nice.

It will be great using React in web app development.

Would also be nice to namespace react specific props like key and ref. e.g.

<Foo @key="foo" @ref={callback} prop="hi" />

this has been discussed here - https://github.com/facebook/jsx/issues/66

@elado While it may be something we want to do in the future I think it’s out of scope because it affects non DOM-specific API.

While it's exciting, especially the reduction in bundle size which is always welcomed. My worry is that the folks at Facebook are underestimating the inertia, pain and fragmentation breaking API changes can cause to the ecosystem and community.

It's true that they've large number of components at FB to worry about, but the advantage they got, and I assume here, is their minimal dependency on external open source react libraries since they most likely built most of their components internally. For the other folks who uses a lot of open source libraries, it really took a big deal of effort, waiting and frustration to get Proptypes out of many libraries (and I speak from a real experience of upgrading a large react code base). Moving fast and breaking things might not be applicable for the folks who want to build stable business with limited resources.

I really enjoy react, the API is simple and it personally took me no time to learn and adjust to it's syntax. Thus I really urge the maintainer to evaluate the pros/cons of any ecosystem wide breaking changes carefully and think pragmatically if the change warrant forcing the whole ecosystem for re-write.

Other than that, I really appreciate the effort of building, maintaining and upgrading React, and the passion of the engineers behind it :)

These future {...updates} are 🔥

className -> class: I don't think it's a rare situation when the class attribute is defined outside of JSX/object declaration. In such situations we will have to use different naming for the same thing, itroducing inconsistency, increasing code complexity and cognitive load.
The suggested change won't remove attribute/prop name mismatch from our codebases - it will exist somewhere due to the syntax limitations or when working with native DOM. It's been a good thing that we have "standardized" (and well established at this point) workaround for this naming problem.
Currently className can be used consistently everywhere, always refering to the same thing - even when working with native DOM. Actually the react may be literally the only place, where we will be forced to use class to refer to class attribute.
className seemed quite unnecessary and a bit confusing from start, but turned out to be quite practical later and relatively easy to get used to.

I would also get rid of the extra object { __html } for dangerouslySetInnerHTML as it seems really unnecessary, but it may not be worth the problems related to migration (and perhaps unrelated to this issue altogether).

While the other changes generate a lot less emotions, they're much more interesting and important, so thank you fb team for the effort.

The more breaking changes, the longer it will take to upgrade actual user applications. Personally I'm already quite a long way behind latest greatest React (which saddens me), because previous versions of React have broken stuff which then requires libraries I depend on to upgrade, which then introduces delay.

Ideally I'd like to be in the scenario that:

  • Downstream React library rewrites code on new version, introducing bug for my use case
  • React adds cool new feature / bundle reduction
  • Old library version remains usable on new React
  • Yay I can use new feature right after React launches and just wait for the library to fix the bug

In practice it's more like:

  • Library rewrites code, introducing bug
  • React releases new breaking change
  • Old library no longer works
  • New library eventually updates
  • Bug may still be present
  • I am stuck on outdated React rather than (to quote Ken Wheeler) "the new hotness" a long time.
  • I feel sad :(

From a 'shipping code perspective', every major breaking upgrade becomes a huge piece of annoying technical debt that involves updating all the dependencies, re-testing everything, then hunting down new bugs to see if there's a work-around. Unless there's a critical vulnerability or it's truly impractical to do something (and that bar is high) it gets de-prioritised.

Unfortunately, despite having shed it's 0.x paradigm and despite trying to be a core web library React is still churning version numbers meaning lots of time spent running to stand still.

Basically what I'm saying is, please, please try to plan an API that allows future big changes not to be breaking.

(Also the IE11 thing is sad because you're deprecating a browser that's not EOL - last release 52 days ago. I think the React team will find this just pushes the work to the Facebook team and all external and internal library teams to mitigate it instead).

@urugator

I would also get rid of the extra object { __html } for dangerouslySetInnerHTML as it seems really unnecessary

This is intentionally designed to be explicit and difficult to use accidentally. If it's removed, then any unsanitized data can accidentally be passed to dangerouslySetInnerHTML.

@philipwhiuk

Basically what I'm saying is, please, please try to plan an API that allows future big changes not to be breaking.

Breaking changes are necessary to improve technology, and React does it responsibly with infrequent batches of breaking changes. Without breaking changes like removing componentWillReceieveProps, we wouldn't have great things like React Suspense, and software in the community would be more difficult to maintain with old APIs staying around forever. If you upgrade one major release at a time, you should be fine.

Also the IE11 thing is sad because you're deprecating a browser that's not EOL - last release 52 days ago.

Nobody is deprecating IE 11. React's current version actually requires polyfills for some features in IE 11, so the suggested changes would likely have about the same effect on IE 11 development. https://reactjs.org/docs/javascript-environment-requirements.html

I would also get rid of the extra object { __html } for dangerouslySetInnerHTML as it seems really unnecessary

@urugator - For what it's worth, internally at Facebook the most common usage of __html is to insert server-rendered HTML into a React component tree. For that use case, we construct the __html objects server-side, and have lint rules against doing it client-side. Server-side, we have methods that serialize XHP to objects with __html properties. That ensures we don't introduce any security holes anywhere, as XHP is very similar to JSX (it was actually the main inspiration for JSX) and also has XSS protection.

Essentially, the __html objects mark a string of HTML that we know has been sanitized somewhere and is safe to directly insert into the DOM. A raw string is harder to handle - How can you tell if it's been sanitized vs if someone accidentally returned some raw user input (and thus introduced an XSS hole)?

So yes, it's intentionally difficult to use, because there shouldn't be many use cases in most apps, and the use cases should be reasonably contained. Generally you shouldn't construct the __html object directly in your React component, rather you should have some function that returns it (see how the docs use a createMarkup function: https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml)

My worry is that the folks at Facebook are underestimating the inertia, pain and fragmentation breaking API changes can cause to the ecosystem and community.

Thanks for bringing this up. I totally understand where you're coming from. But I'd like to point out that the majority of people on the React team have been using React themselves outside of Facebook before getting hired — often on large applications. So I think this helps us empathize with the kinds of issues that affect our open source users. I've used React for two years and remember the fragmentation caused by e.g. parent/owner context switch very well. The key was having a good migration strategy — and the same applies to all of the above changes.

It's true that they've large number of components at FB to worry about, but the advantage they got, and I assume here, is their minimal dependency on external open source react libraries since they most likely built most of their components internally.

This has historically been true for the product side of development. However, since Yarn was created and integrated with our web repository, we have seen an increasing number of third-party components used in internal tool pages — which by themselves have a much larger surface area than Facebook consumer products. So we'll need a strategy for gradual migration in third party packages too.

Unlike many other companies, we use the same version of React across the whole codebase. So we also have unique challenges (e.g. we can't just "wait" with upgrading some products). This will certainly add more pressure on us to have a good migration strategy and serve as a stress test.

For the other folks who uses a lot of open source libraries, it really took a big deal of effort, waiting and frustration to get Proptypes out of many libraries (and I speak from a real experience of upgrading a large react code base). Moving fast and breaking things might not be applicable for the folks who want to build stable business with limited resources.

In case you're curious, we still have code like

React.PropTypes = require('prop-types')
React.createClass = require('create-react-class')

in our codebase because we can't afford to migrate to a "clean slate" fully either. But people starting with React today, as well as more modern parts of our codebase, don't have to carry that weight. That's kind of how we think about it — we want to choose better defaults for new React users, but have a good migration story for ourselves and everyone else, even if some parts of it might be hacky.

I might not have communicated this clear enough so I apologize. Any large change will need a sophisticated and well-executed migration strategy that takes into account that third-party libraries will take a long time to update, different versions might need to coexist, etc. For example, with class the plan isn't just to change it overnight. If we want to do it, we'll want to be smart about how to roll it out — possibly with a grace period where we allow both, or a helper to make it easier to do it, and then gradually phasing out the old name. I don’t have a specific strategy yet but it’s clear that the change is just infeasible without a good strategy — both for us and for you. This is something we take very seriously.

(Also the IE11 thing is sad because you're deprecating a browser that's not EOL - last release 52 days ago. I think the React team will find this just pushes the work to the Facebook team and all external and internal library teams to mitigate it instead).

I don't understand what you're referring to. I didn't say anywhere IE11 was getting "deprecated".

My post only says that we might require more polyfills than now for browsers like IE11. React already requires some polyfills in IE11. I'm not saying we want to break it — it still has many users — but that you'll need to include more polyfills if you want it to work. That seems entirely fair to me.

Also, we don't get to "push the work" to other teams at Facebook. If we break something, it's on us to fix it. This is why we care so much about migration strategies — we typically have to execute them ourselves.

Re: the rest of your comment — we absolutely don't make breaking changes for the sake of it. In fact we're trying very, very hard to avoid breaking changes. There are many parts of React that are complicated and hard to maintain, but we keep them for the sole reason that we're trying to support legacy APIs even if they have been explicitly marked as unstable. However, at some point the weight of issues accumulates, and we need to clean the slate to move forward and fix them. The issues in my OP post are exactly those kinds of problems. They wouldn't be worth doing a breaking change alone by themselves. But combined, we think they're worth the effort.

I don´t get why people are so negative to this. Just don´t upgrade until your dependencies do?

Is the keyword issue for class vs className and for vs htmlFor not something that the jsx compiler can handle to override the reserved words?

I would really only be concerned if the changes are big enough that search results give obsolete answers... Like the pain angular 2+ introduced when googling for"angular -angularjs -angular1.x" etc

Other than that I welcome all the changes!!!

@gaearon

You said

"We may need to drop compatibility with some older browsers"

It was the drop compatibility part I had a problem with, not the following polyfill part. Going back to the dark ages was my worry.

Regarding process. Isn't this sort of 'issue' what the RFC process was designed for?

It was the drop compatibility part I had a problem with, not the following polyfill part. Going back to the dark ages was my worry.

I was talking about IE10 and earlier. I specifically noted we want to keep supporting IE11 — I think it represents about 3% of our traffic, which is much more than the 1% cutoff where we consider it too old.

Regarding process. Isn't this sort of 'issue' what the RFC process was designed for?

We'll post RFCs when things are more fleshed out. A lot of this requires exploration, experimentation, and actual testing to determine what we can and cannot do, and what migration strategies we could use. We don't have enough details right now to even begin thinking about RFCs (which should include a comprehensive migration strategy).

Ok thanks for the clarification!

@gaearon

It's plausible that we'll get rid of synthetic events altogether.

How would that work with event.currentTarget pointing to the element on which the event listener is attached, which in the native case always means document - or the React root once React switches to that?

React Is Not Just Setting Properties

(...)

My point here is that whether React uses properties or attributes internally is an implementation detail

Why is this an implementation detail when it can influence what happens in the DOM? Unless that referred only to properties that are somehow reflected in attributes (and back) like class/className.

I feel the confusion around the treatment of class/className is related to the fact that whether React uses a property or an attribute is considered an implementation detail. I have an Angular background and when I started using React it was the biggest gotcha for me - in Angular the separation between attributes, properties, and event handlers is clear directly from syntax and I was confused whether props on DOM elements are set by React as properties or attributes.

How would that work with event.currentTarget pointing to the element on which the event listener is attached, which in the native case always means document - or the React root once React switches to that?

I don't know yet. :-) We'll see what we can do. It's entirely possible that some of these things won't work out, or will come out differently. This plan is just a rough outline of things we plan to look into, and the unifying vision for them.

Why is this an implementation detail when it can influence what happens in the DOM? Unless that referred only to properties that are somehow reflected in attributes (and back) like class/className.

For majority of them there's no observable difference from user's perspective which one to use. I think most of them are reflected (although maybe I'm wrong).

I have an Angular background and when I started using React it the biggest gotcha for me - in Angular the separation between attributes, properties, and event handlers is clear directly from syntax and I was confused whether props on DOM elements are set by React as properties or attributes.

I appreciate your perspective. However, in practice this difference often is irrelevant for most DOM elements and it's debatable that learning ins and outs of which one to use, and always having to think about it, is actually valuable for everyday product development. I think the degree to which people confuse them speaks to the fact that it's not actually that much relevant to them.

(There are cases where it becomes much more relevant such as with custom elements. But even there, supporting both equally is just as confusing because you have to pick which one to use. This has been debated elsewhere so I'd like to avoid jumping into this debate again — we have proposals from folks like @robdodson and we'll look into them.)

@Daniel15
Without the mentioned policies (which I don't think are mentioned anywhere else), the extra object wrapper doesn't make it safer in any way.
I believe the user is quite sufficiently warned about the dangerous usage via dangerouslySetInnerHTML. That's the point when the user si forced to check the docs, consider the implications and make the decision.
The need to wrap the (possibly unsanitized) string into an object/function whatever, won't make him to reconsider or sanitize the wrapped value.
If it would work that way, then perhaps { __html } isn't complicated enought and [{ _html: { __html }] would make it even safer - how many times we have to state that something is dangerous in order to make it safe?

With the insight you provided I understand the reasoning, but I think it currently doesn't apply to anyone outside of facebook, because we don't have a clue about "{ __html } represents sanitized html" rule.
I always thought it's just another obstruction in the API. Thank you for taking time and sheding some light on it, makes more sense now.

It's not just about making it more complex, it's about preventing non-sanitized data from being used accidentally.

I just love the way react is so opinionated by the community. Kudos for you guys to hear it all and make the best out of it.

I love how everybody thinks the changes that are coming here or came to React in the last versions were 'breaking'. Those people clearly didn't experience the Angular 2 > 3 > 4 times.

I love the changes. Personally I don't mind className and destructuring class could become a hassle. But I would love to see what you guys are gonna come up with.

I’d like to ask to keep this discussion on topic. Whether you find tainting dangerous HTML helpful or not is interesting but not related to the issue.

i love React

This is a perfect chance to help developers new to react feel more welcome. I'm very glad this is happening.

Just letting people use class has no negative effects except it doesn’t work with destructuring, and the migration cost.

@gaearon Throwing in 2c but those two negatives seem as big or bigger than the current negative (a one-time small cost of learning class => className vs always avoiding destructuring + ecosystem-level migration cost).

React :heart:

echoing @natew above:

@gaearon Throwing in 2c but those two negatives seem as big or bigger than the current negative (a one-time small cost of learning class => className vs always avoiding destructuring + ecosystem-level migration cost).

Anyway, what is the motivation behind changing from className to class ?
As far as I can glean your comment boils down to two arguments, (please correct me if I'm wrong):

closer conceptually to what most people expect

Ok but adding Name is the tiniest of hurdles. Learning to use className was the simplest part of learning React. It's a little quirk that has no downsides - indeed it actually solves a problem with four characters. And it has way less cognitive overhead than any of the destructuring alternatives provided.
Imagine teaching someone to style their first React component with

function Button({ color, ...rest }) {
  const buttonClass = rest.class +  ' Button-' + color;
  return <button {...rest} class={buttonClass} />
}

My noob head would have exploded.
I think that when interacting with an API people expect the API to provide solutions. ClassName is a built-in solution. Class is a built-in problem.

less typing

Bruh, it's four characters 8^) . Besides, even if we destructure class out far less frequently than we currently use className it's way more typing when we do. So I'll have typed 4 fewer characters 12 times but added 50 more characters each of the times I desctructure class? This is just silly.

Is there another compelling reason for this change that I've missed ?
Is the motivation some kind of concern about conformance with DOM attributes/properties ?
That seems like far too academic an argument to me. Most people are not very aware of these implementation details and I don't think they need to be.

Is it that if released today React would allow class instead of changing to className?
I think that is irrelevant.
React was not open sourced today and if it were/would be open sourced a year into the future a different set of decisions might be made at that time.
Sometimes it's better to stick to consistency than try to iron out every wrinkle.

And if React had been using class from the beginning we would have gotten used to destructuring class and reassigning it to className but whomever came around with a PR for changing class _to_ className would be hailed a savior.

Lastly, I just want to say that I did not expect myself to get so worked up over this, so I apologize if anything came across as rude, insulting, or accusatory. I'm thankful for all the work you do on React and for all the knowledge you drop on Twitter - I've used your tweets to defend some answers I've given on interviews.

I pray to dan.church that you change your mind.

I am very happy that react can be continuously upgraded.As a developer of using react,i don't want to see it being overtaken by vue. But the process of className-> class must be painful.

I'm not against the className -> class renaming for copy-paste's sake but there isn't a clear motivation for it as of now. className exists in the DOM while there are reserved prop names that comes out of nowhere like htmlFor. I feel like those should be prioritized when it comes to renaming and that probably is less pervasive of a change which can serve as a test as well.

@sonhanguyen htmlFor is the official web api name as well: https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/htmlFor

@allan2coder instead of wrapping it in an array, wrap it in <React.Fragment> or <>.

@allan2coder Also please let's keep this thread on topic, you can ask questions by filing a separate issue if you'd like.

I'd like to draw attention to patch-package which is a package that makes it rather easy to patch your dependencies (e.g. to make them stop using unsupported React APIs). Admittedly, this is more useful for application code than library code, but I think it should help address some of @philipwhiuk's concerns.

I'm looking forward to。

  • Migrate from onChange to onInput and don’t polyfill it for uncontrolled components.

For the above words, I‘m curious will the onChange event be used close to native event or not used in the future?

@MuYunyun

Based on the wording, they don't plan on keeping the current behavior of onChange. It wouldn't make much sense. React like other modern libs won't be a browser compatibility layer, nor should it be.

The current onChange emulates onput + a few other cases. It doesn't make sense at all considering a change event already exists in the DOM and it doesn't have the same semantics: https://developer.mozilla.org/en-US/docs/Web/Events/change

@jxub

A bit offtop but it's kinda sad that no one (as far as I know of) had the idea to make a html/css/svg -> jsx transfomer in order to ease migrations to React with so many trivial changes to map HTML attrs to React props. So many man-hours wasted perfoming mostly find-and-replace :(

I don't know about other editors, but IntelliJ IDEA (and WebStorm, PhpStorm etc.) does this transformation, when you paste a HTML code to JS.

A bit offtop but it's kinda sad that no one (as far as I know of) had the idea to make a html/css/svg -> jsx transfomer in order to ease migrations to React with so many trivial changes to map HTML attrs to React props. So many man-hours wasted perfoming mostly find-and-replace :(

It actually exists but it's pretty old and not entirely correct: https://magic.reactjs.net/htmltojsx.htm

We should revive that effort. If you want to help please do: https://github.com/reactjs/reactjs.org/issues/484. So far nobody offered to help and followed up.

It actually exists but it's pretty old and not entirely correct: https://magic.reactjs.net/htmltojsx.htm

I've used this a ton of times before - it's very common to use when outsourcing certain components to people who know HTML/CSS but not JavaScript.

So just giving feedback that this is a useful tool that people use in practice. There are also other converters online.

A problem with this approach is that it's only one-way (only migrate _to_ react). That problem isn't special to React though.

Hope it will make things much easier...

A problem with this approach is that it's only one-way (only migrate to react). That problem isn't special to React though.

Why? It should be even easier the other way around — just run ReactDOMServer.renderToString.

Patiently waiting for the release

@gaearon

Why? It should be even easier the other way around — just run ReactDOMServer.renderToString.

Ok, so for context the problem discussed is: I want people who write html/css and don't know JavaScript well enough to work on the JS code to be able to work on the markup/style/layout of components.

One way to do this right now is to have them develop it in html/css and use htmltojsx. That works pretty well for me.

The problem is I now have to maintain every markup/style/layout change myself in React.

If I call ReactDOMServer.renderToString indeed I'd get static HTML out but that HTML would be missing all the actual component code so it's not feasible to give it to UI people and use their output as I'd have to rewrite all my logic.

Things like react-sketchapp are a nice possible take on the problem but I'd hope for a more generic approach letting me do this while using regular html/css. For example - through some form of metadata and keeping react IDs on elements.

I totally get this isn't in scope for ReactDOM core but it's definitely in its domain and is an interesting problem.

This isn't high priority - I mostly just wanted to explain why htmltojsx is useful to some of us even outside of the context of a migration :)

I want people who write html/css and don't know JavaScript well enough to work on the JS code to be able to work on the markup/style/layout of components.

How big of a problem is this, and how hard would it be to teach them a little JSX? The changes need to be reviewed by a React-savvy engineer anyway.

Sorry if this gets too off-topic, but I believe just HTML/CSS is not enough nowadays.

(@gaearon feel free to hide this if it gets noisy as it's a reply about the above already slight OT comment)

How big of a problem is this, and how hard would it be to teach them a little JSX? The changes need to be reviewed by a React-savvy engineer anyway.

Well, it would require the following:

  • Only work with contractors that are familiar with your current React stack. It's far harder to find people who'll learn styled-components
  • Install Node (npm), your stack (React, webpack etc), and everything else required to run it project on each of the UI peoples' computer and keep it up to date.
  • Teach them some basic programming and JavaScript, learning how to work with the devtools, console errors etc.
  • Teach them React, what its syntax is, stuff like className, what key means etc.
  • Only work with contractors and freelancers who are willing to work that way rather than producing HTML/CSS. This usually also makes things more expensive.

Now, like mentioned above things like react-skeptchapp are a useful way to make this more approachable and easy for UI people - but it still requires them to learn a lot.

I totally get why you're asking this and I'm not sure this is a common problem (I suspect it is). Regardless I don't think it's in scope for the future of ReactDOM's core.

This is quite off topic :-) Let's continue this discussion on Twitter or somewhere else.

"The behavior you see a tool being used for is a behavior that tool encourages"
~ Gary Bernhardt

The way React is being used, for most of it not being part of React's API, results in a high number of pitfalls that result discussions like this where everyone is confused and frustrated. Is using a reserved keyword such as class bad because of javascript, html or react itself?

Going back to the changes made in 16.3 (If I remember correctly) the library would now behave differently by accepting any attribute passed to native html elements being rendered by the vdom, I remember quite clearly that most of my codebase was now plagued by bugs because of the behavior we had implementing this clearly bad pattern

<div {...this.props} />

So I gotta say im quite disappointed that Dan is suggesting that we keep spreading this terrible code across all codebases.

First of all, there are no guarantees in the contents of props, but at the first time native html elements will almost always not do whats expected of them if you pass the wrong props to it, however at the same time its considered to be safe to propagate parent props down to child components, but in reality this is terribly lazy

<Child {...props} />

const Child = (props) => <div {...props} />

Sometimes you don't even know what you're rendering and this issue becomes even harder to deal with. It is also extremely hard to typecheck these kind of components using type systems because that would mean that most components are required to implement a number of random interfaces they should share between each other, its a deep deep rabbit hole to go down into.

If a rewrite to react is made to fix this I would expect the designers behind it to realize the harm these bad patterns cause to codebase instead of doubling down on bad decisions.

We gotta remember that jsx is built on top of javascript, and using javascript's "idiosyncrasies" as excuses to deliberately conflict with the language in the use of keywords is a bad first step towards fixing the API, I would suggest that instead of starting a war against the language you would move away from it entirely

@gaearon Long time! :) (ps. sorry for tl;dr ahead)

Anyway, I just wanted to give some insight into what I've learned from my own adventures. I've made my own "bare minimal" React derivative which I now use for performance/feature critical applications. It was initially just a fun side experiment but I was seeing performance improvements of beyond +300% in real-world cases for initial rendering/updating/deleting including many others benefits. That's without using a pooled event handler too.

IIRC a large portion of this comes simply from my own library bypassing all the React-props logic and simply feeding attributes straight into setAttribute, styles straight into style.setProperty, listeners straight into addEventListener, and so on. So the basic DOM element interface looks like this `{attributes: {}, style: {}, listeners: {}, className: "", ...}, it's more verbose but the interface is now small and trivial to implement and is extremely fast. This is probably too extreme for you, but it has served me very very well.

Something I've been doing as well is dropping CSS entirely, all styles are inline. It's much much faster than you would expect and comes with a lot of nice practical benefits, it also side-steps the cost of browser initial CSS-parsing (which is surprisingly expensive) and selector matching which recoups most of the overhead. I can easily achieve stable 60 FPS and way beyond on my under-powered laptop for even complex hierarchies (without any tree pruning).

I imagine most of that is not really useful for you, but maybe there's something interesting there. Anyway, my main gripe with HTML+React is that React user components do not expose any interface for layouting/positioning (and probably to some extent also events) without resorting to CSS. To put it simply, if I create a ButtonComponent and then use it, by default it cannot be positioned or integrated into a layout without; implementing purpose-special logic inside it, wrapping it in a dummy element which can be positioned or by exposing a style-prop which is merged into the styles used for ButtonComponent root element. None of those are perfect, and merging styles as-is is dangerous as it's easy to put styles in there that you shouldn't. Essentially, the problem is that at component boundaries, a subset of the available properties should be public (positioning/layouting) and some internal (visual styling).

Another thing I do is separation between components and content/children. E.g. DOM components do not take a list of children, instead it accepts a content instance. The content instance implements the reconciliation logic and rendering of children, which means that you can have different implementations for different purposes. The most obvious use-cases being; mostly static hierarchies vs dynamically generated children. So static hierarchies which make up the majority can be implemented using faster simpler logic, whereas dynamically generated children may have configurable strategies. It also opens up for the possibility of having "content components" which could be used for intelligently managing e.g. flexbox layouts without unnecessary indirection.

Related to that and something I think React gets wrong is children, I haven't arrived at specific conclusion or solution yet. But I strongly believe there's a fundamental problem in React in that you cannot bubble props from the root to an arbitrary point in the hierarchy without re-rendering every single element between those two points and effectively pruning is often problematic too. Performance is mostly "good enough" and the added complexity of managing this in React might not make it worth solving. But in my experiments so far I've been able to reduce full root update costs to mere fractions with very small amounts of trivial code, but my intuition says it's not very compatible with the philosophy of React or its simplistic children model.

Something that's mostly definitely not for you, but a big feature of my library is that it basically only provides a bare minimal "base DOM element" for general purpose. The idea being that you can trivially extend/replace and implement your own low-level specific behaviors if you're developing a large project with specific requirements/circumstances very cheaply and without resorting to "hacks", e.g. if you're very heavily touch-oriented, want to do special style-handling, etc. It also means that different versions are compatible and can be run side-by-side as long as the ABI is the same (a change should be extremely rare and very simple to fix). This also means that you can be quite opinionated in the DOM element interface without having to be all-encompassing and try to solve everyone's problem.

Anyway, perhaps mostly just rambling and I'm probably not conveying most concepts very well, but maybe there's something interesting information for you from someone who went the opposite direction. Most of it is probably not relevant to you simply because you are targeting "simplicity", but who knows :)

Also before I forget, you might be interested in my objectKeyValueReconcile function, specifically optimized to be exceedingly fast for comparing prev/next props for React-like scenarios. It's a responsible for quite significant real-world performance gains for me.

https://github.com/syranide/surgical/blob/master/packages/surgical/private/objectKeyValueReconcile.js

imho I'd drop the "class" idea. JSX will eventually die.

What are the thoughts of adding support for non-element based DOM/window events to React DOM? With the possible removal of synthetic events I suspect there’d still be some form of event collecting/updating/batching. The keypress, resize, and window scroll events are common scenarios that come to mind, but possibly being able to support most/all of the list in https://developer.mozilla.org/en-US/docs/Web/Events would be beneficial behind the same abstraction.

This happens to also be discussed in the oldest open issue #285 😄.

@gaearon

The practical effect of className -> class will be:

({ className }) => <div className={className} />

will become

({ ...rest }) => <div class={rest.class} />

This change will cause quite a bit of pain while the benefits are entirely academic. We have seen other communities do similar things - eg, consider that python3 was first released a decade ago and we are still arguing about it. A sizeable portion of the community has not made the transition and never will. Please reconsider.

@kans

Or this:

props => <div class={props.class} />

That’s of similar complexity to the original function.

I liked className because class is a keyword in js, but don't think it's that big a deal either way. I prefer styling with something like glamorous, so, this isn't something that affects me. I've maybe used className a couple dozen times the past few years?? Almost never.

Though one thought that occurred to me in favor of class is that most people who actually make use of className are using css, and they expect the html/css model of class. The overwhelmingly main purpose of this prop really is just linking with css code, right, so why not just call it class like an html/css user expects?

IMO outside of design system code, really idiomatic React development wouldn't typically be using className or class very much anyway. When I do need to use classes for css, it's mostly isolated to a few small UI components. Pretty much anything used at the application layer is higher level (like, say, <Columns verticalAlign="center"> or <BodyText />).

So I guess I wonder if renaming from className to class makes it easier for the kind of people who use the prop anyway (easier to get into React development, to switch contexts, to not get angry, etc), why not just rename it?

Reduce the bundle size please.

Since this is a big, breaking update, perhaps we could fix React lifecycle naming too!

i.e shouldComponentUpdate => shouldUpdate, etc. This naming is beyond silly.

@AlexGalays

Since this is a big, breaking update, perhaps we could fix React lifecycle naming too!

i.e shouldComponentUpdate => shouldUpdate, etc. This naming is beyond silly.

Longer lifecycle hook names are less ambiguous when searched for. Shorter names are too generic and depend on the context where they are located, so may coincidentally match something else a large codebase (false positive match).

Lifecycle methods are part of React core, and this issue is solely about react-dom.

@ljharb
Oops, indeed !

@sompylasar
Sorry, that's simply not a good reason (pun intended, reasonReact fixed the method names :)). We don't prefix all our modules by their package names as well and we find them just fine. Anyway, going to stop talking about this since react-core is out of scope in this issue.

Would love to see these changes.
I hope they will be implemented with custom-elements interop in mind.

I wonder if it's worth moving some of the discussions to separate threads in the forum (https://discuss.reactjs.org)? Since GitHub issues aren't threaded like forum posts, it makes it tricky to discuss multiple different things in the same issue.

The changed of className -> class seems interesting to me 😺

Watch, 2019 React projects

const {
  class: className, // YouKnowIamRight PepeHands
  someFancyProp,
  ...restProps
} = props

I see this coming for sure.

I totally love to be able to copy paste HTML and not having to change className but at the same time I see the issues that class reserved keyword could bring.

At this point, I would prefer to stick with className unless you have a strong engineering concern about it (not personal preference)

But that is just my pragmatic opinion.

While I understand the additional friction of not being able to destruct class identifier, I feel like too many people in this thread overestimate the number of times they need to receive class name as a prop.

Passing className to a React component (that is not just DOM element) means you either making quite small building blocks without providing any new abstractions or that component expose some of its implementation details. In the first case (e.g. <Button /> component) you probably would want to follow @gaearon's example and forward the "rest" of props to the DOM element as well. In the second case, maybe the additional friction would force you to avoid doing the thing and come up with better solution.

From my humble experience, I had to copy paste HTML to JSX (and replace class with className) more times than I can remember making a component that receives className.

Spreading all props to a HTML is an anti pattern. If someone in the future includes an extra prop that happens to match a HTML attribute it will unexpectedly change the behaviour. If IE adds a new attribute you are hosed.

@philipwhiuk if you only spread the props you don’t use there won’t be any behavior problems.

@j-f1

if you only spread the props you don’t use there won’t be any behavior problems.

Not really. If you add a new option to your component API then this option stops being passed to the underlying DOM node. Someone may depend on that exact attribute being set on the element and then you break that person. Spreading all unused props to anything underneath means any addition to your component API is a breaking change.

Btw, I used a random example where you deconstruct the value of class but don't get cut off on that detail.

Many of you (yes, you do) deconstruct almost every value from the props and state instead of using the dot notation for non-reasons other than avoiding to type some extra text.

So that is why I am making the emphasis on the situation, but do not go sideways and focusing in spreading props or whichever another side topic you could talk about it.

If you are a little bit more pragmatic you would talk about the technical implications instead of your personal preferences but I don't see anyone saying anything about technical implications of className.

Another thing you need to pay attention to is that you are making the Core contributors focus on something that is the matter of personal preferences (prove me wrong, just because I want you to think about it pragmatically)

Their time, effort, and money (from the company) are limited so better if the community understand that there are better things to focus our attention to and if anyone from the Core team will expend an hour on something we prefer that they used it in the next big thing for React, no refactoring code for such of change.

But again, my pragmatic opinion.

Speaking of polyfills, I don't think react should try to detect whether the user specified "good" polyfills.
(e.g hasBadMapPolyfill in the code)
React shouldn't be held responsible for all the random mistakes one can make in a programming project.

@AlexGalays React relies on using a Map implementation with correct semantics. If users are using a non-compliant polyfill, their app may break in unexpected and confusing ways, so its helpful to warn ahead of time in that case.

This is only done in the development build as well, so there's no overhead for this in production.

I love this React Fire and You would continue working on this as it seems like more interesting step to modernize the ReactDOM.

Would it be possible for react fire to not rebind event handlers?

class Compo exntends Compoent {
 onClick() {
   ...
 }
  render() {
   <button onClick={this.onClick} >click me </button>
  }
}

I don't think anyone actually want the event handler to be rebound. And in the rare cases where you would you could bind it manually

@cullophid

Would it be possible for react fire to not rebind event handlers?

You can bind the method to the class once using arrow function auto bindings or binding the function in your constructor. If there's unique parameters you want to chain on to the event, you can look in to memoizing the event handlers or attaching DOM attributes to the nodes themselves and access them through event.currentTarget.

class Compo extends Compoent {
 onClick = () => {
   ...
 }
 render() {
   <button onClick={this.onClick}>click me</button>
 }
}

Another benefit of simplifying the event system (can we go back to raw DOM events without any pooling like ALL frameworks do? :) ) is that it's easier to statically type as well.

Seems like a great plan! It would be really nice to have IE 11 support (even if that means shipping our own polyfills which is totally fine). As someone who works with government clients, when they will stop using IE 11 is a complete unknown :(

@MatthewHerbst same boat, our clients do use IE 11 and we have enough traffic of it 😢

Just wanted to chime in to say this is all super exciting. The one thing I read that worried me was:

it's possible that we will not attempt to smooth over some of the existing browser differences

Right now I love knowing that React's event system will "Just Work" on any browser that React supports. I'm afraid that this change will mean that it will be become the app's responsibility to account for cross browser issues. This would likely introduce a lot of difficult to trace bugs. ...at least it would for my apps. :)

Anyway, thanks as always for the great library.

@kentcdodds had mentioned JSX 2.0 integration as a possible idea I was reading through the Strategy and did not see anything it seemed related to that. I was wondering if that was just up in the air or something that will be held off for the future?

Let the IE die! Don't support absolutely outdated browsers. There is no reason, even for restrictive companies, not to switch to an up to date browser. You will see that IE11 in your stats until you and the web stops supporting it.

@hbroer Legacy software?

@hbroer nobody stays on an older browser because their sites still work, they do it because they either have no choice, or don’t know how to update. Breaking a site for these users is exceedingly hostile and leaves them not with an updated browser, but with nothing.

  • "don’t know how to update" - not our problem, but we can put in messages "install a new browser" with a list of browsers and maybe with help text and phone number of the Microsoft Hotline ^^
  • "have no choice" - Right, but the organisation has to update their environment. If their tools don't work, they will.

i hate to code for the past and can't use the benefit of new tech, because 90% of coders still support the crappy browsers of the bottom 15% consumers living in last century. ^^ Don't wanna have this "oh what is with this internet explorer 6 nerds, we have to support that 15%" again for the next 10 years.

btw M$ seams to bring Edge for Windows 7 and 8 with their switch to webkit.

@hbroer everything is our problem if it impacts users. Have some empathy for other humans.

(Separately, M$ is a pretty juvenile and very dated late-90s reference that you may wish to remove; I’m happy to remove this aside if you do)

I see, here are many M$ fanboiz. :D I really care on humans, but not on organisations which block technology progression, or the coders who mean to have to support that old crap over years. I have no problem with an additional package which makes a library "old crap compatible". But a library in 2019 should coded with >=2019 tech in mind, and not <=2013 tech. It is bad enough to have to support that (oh let us do this a little bit another way) safari and (old) edge crap.

micdrop

@hbroer in fact ancient android browser, and ancient safari, are more of a problem than any microsoft browser. it's not about being a "fanboi" but being professional, and using the term "M$" is not.

And no, it's not "bad" in any way to support old tech, it's the moral and ethical thing to do. What's bad is websites that "work best in X", whether that's Netscape Navigator, IE 6, or latest Chrome.

I see, here are many M$ fanboiz.

@hbroer I see here a person with blanket ad hominem attacks.

But a library in 2019 should coded with >=2019 tech in mind, and not <=2013 tech.

No, it shouldn't.

If you took one seconds and looked into browserlist defaults, you'd be surprised. Instead, you resort to ad hominem attacks and school cafeteria-level insults.

Once you get enough users, IE11 becomes a sizeable chunk of people who access your website. At a previous job we had close to a million people a month accessing our website from IE11.

browsers

If u want support old crap add poly fills. No reason not to code for the future and the present. Supporting old tech makes web apps more fat and slower than needed.

P.s. i see a person who cant read sarcasm and don't care about smilies ^^

@hbroer You're showing that IE11 is the present, so we are expected to be coding for that.

And, once again. See the default value at https://browserl.ist.

P.s. i see a person who cant read sarcasm and don't care about smilies ^^

Yeah, no. This is a common tactic employed by trolls and playground bullies. "What?! I didn't insult you! It was just a joke!".

0.20% of people on the planet, even filtered by "those who use the internet", is 6.4 million human beings. Percentages are utterly irrelevant. Code for humans; the past and the future, and your bundle size, doesn't matter.

If you want to code for humans bundle size, just like browser compatibility, certainly matters. Users not only expects things to work, it should be fast to load as well.

Now in 2019 i will use for new projects css grid (which has only experimental support on IE11), and will transpile to ES6 and i don't want deliver polyfills for that outdated Browser. IE11 Users simply get a message: Update your browser or use an alternative.

There are people out there who don't want use java script. You care about them? You don't see that guys in the stats. And i am not in statistics too, like many other people who block that tools.

I take a eye on every browser which is not outdated. I support edge, which has less users than that IE11 crap, because of Windows 7 (which supports ends january 2020), and modern people use modern browsers. ^^

Nobody stops you from using polyfills and something like a compatibility package. But the core should be up to date and not stay way behind only because of one piece of an old M$ tech browser.

What i am missing in many javascript frameworks is LTS. Thats what we can talk about. If you build a modern page with features from the present, then its nice to use a up to date tech framework. And if u are building a webapp for b2b stuff which needs the maximum of stability and compatibility then u can use a LTS version. Or use knockout. Then u can support the few 100t people who are still using an not updated windows XP with IE6 ^^

Compatibility is clearly listed under "trade-offs" in the original post so having perfect support for old browsers is already a secondary focus.

They will require more polyfills and integration if you need to support them but that would be your choice to do so. You can also create multiple bundles for different browser targets and deliver the smallest possible JS for each visitor so it won't affect your whole audience.

The progress on React Fire itself seems unaffected so there's really nothing to be gained from this debate. Let's move on please and get back to the main topic.

Would this be a good opportunity to resolve #6410? It seems similar in impact to the proposed changes to onChange / onInput.

Update from June 5th, 2019

It's been a while. Here's a small update on where we are.

We started working on Fire in December. There is some work-in-progress in https://github.com/facebook/react/pull/14382 and other threads. However, as we started removing parts of the event system that we thought were unnecessary or outdated, we discovered many edge cases where it was being very helpful and prevented bugs — even in modern browsers. We still want to cut down the legacy bloat but it's not clear that being closer to raw DOM events is the best direction in practice. Reducing library code only to re-add it several times in the application code is not the best tradeoff. And it's not even always possible to fix it at the app level.

Separately, while working on FB5, we realized that even when using React, today there are significant difficulties in implementing user interfaces that work and feel great on both mouse and touch devices. Even basic things like buttons feel very different with mouse and touch when you use events like onClick, and it gets even harder to achieve a consistent behavior with hover, focus, etc.

So when we started work on Fire, we thought that maybe a custom event system is unnecessary. But after prototyping removing it, we realized that our event system is in fact our biggest lever to fix this set of problems.

As a result of this investigation, we have temporarily paused work on other items that are part of React Fire, and decided to focus just on the event system first. That project became known as React Flare (https://github.com/facebook/react/issues/15257). It is challenging enough by itself and affects the other items on this list, so we are focusing on it first in isolation.

The goal of React Flare is to make it easy to build UIs that feel great on desktop and mobile, with mouse and touch, and that are accessible. It includes declarative APIs for managing interactions like Press, Hover, and Focus. Unlike with the current React event system, the Flare design doesn't inflate the bundle for events you don't use — and it should allow to cut down the amount of code in UI libraries that deal with mouse and touch events.

React Flare is still an experiment, but we're fairly confident in its overall direction, and plan to eventually make it officially available in open source. (For now it only works if you manually build from master, and there are no semver guarantees as we're actively working on it.) Like Fire, "Flare" is just a codename — by the time we ship it, it will have proper naming, documentation, etc. Maybe react/events or something like this. We'd like to offer equivalent events in React Native too eventually.

After we've finished the initial implementation of React Flare, we will come back to the React Fire list and reevaluate all the other points using what we've learned from it. It is still likely that if Flare takes over a "richer" set of events, we can simplify the "basic" event handling in React DOM and remove a bunch of polyfills. Thanks to everyone for the discussion so far, and I hope this is useful.

Great work! So the event system is still necessary to balance mouse and touch events, but it will be lightweight and independent from React.

Will "React Flare" come as a part of the default React package or need an additional install considering the amount of API's it will ship with?

We'd like to avoid unused code getting bundled. So the current intention is for it to be opt-in per API. Maybe separate entry points in a single package.

Will this mouse and touch event system leverage PointerEvent? I did not see a mention of this web standard in the previous update, so I just wanted to bring it to your attention.

Pointer events are DOM events that are fired for a pointing device. They are designed to create a single DOM event model to handle pointing input devices such as a mouse, pen/stylus or touch (such as one or more fingers). The pointer is a hardware-agnostic device that can target a specific set of screen coordinates. Having a single event model for pointers can simplify creating Web sites and applications and provide a good user experience regardless of the user's hardware.

And here is a direct link to the current browser compatibility.

@jonathantneal Yes, the new system heavily use Pointer Events – with fallbacks to Mouse/Touch events when there's no support for Pointer Events.

I am concerned that https://github.com/facebook/react/issues/11347 was not addressed in this issue. React flunks https://custom-elements-everywhere.com.

Please consider shadow root when redesigning the event system - just attaching to the React root wouldn't solve most issues today, only attaching to the element (https://github.com/facebook/react/issues/9242, https://github.com/facebook/react/issues/15759, https://github.com/facebook/react/issues/13713, https://github.com/facebook/react/issues/11827)

In this update: https://github.com/facebook/react/issues/13525#issuecomment-499196939 @gaearon mentions:

However, as we started removing parts of the event system that we thought were unnecessary or outdated, we discovered many edge cases where it was being very helpful and prevented bugs — even in modern browsers.

I was curious if a list of these edge cases are documented anywhere?

@gaearon now that Flare has gone out (SCNR), is there an updated plan (regarding the June 5th, 2019 update) how to proceed?

And like @trusktr, I also would like to get #11347 addressed here.

Could be split polyfills into another bundle especially the one not relevant to major evergreen browsers.

Hey all, it's been a while and we've tried some of these things on and off.

Let me give an update on each:

We still want to do this, but we've decided to "reserve" React 17 to have minimal possible breaking changes so that it can focus on the next item on this list. So this change will wait until React 18.

  • Attach events at the React root rather than the document (https://github.com/facebook/react/issues/2043). Attaching event handlers to the document becomes an issue when embedding React apps into larger systems. The Atom editor was one of the first cases that bumped into this. Any big website also eventually develops very complex edge cases related to stopPropagation interacting with non-React code or across React roots (https://github.com/facebook/react/issues/8693, https://github.com/facebook/react/pull/8117, https://github.com/facebook/react/issues/12518). We will also want to attach events eagerly to every root so that we can do less runtime checks during updates.

We're doing this in React 17. This turned out to be a huge chunk of work but thankfully it's finished.

  • Migrate from onChange to onInput and don’t polyfill it for uncontrolled components (https://github.com/facebook/react/issues/9657). See the linked issue for a detailed plan. It has been confusing that React uses a different event name for what's known as input event in the DOM. While we generally avoid making big changes like this without significant benefit, in this case we also want to change the behavior to remove some complexity that's only necessary for edge cases like mutating controlled inputs. So it makes sense to do these two changes together, and use that as an opportunity to make onInput and onChange work exactly how the DOM events do for uncontrolled components.

We'll likely come back to this but it's unclear how much churn is worth doing here. So this is still TBD.

  • Drastically simplify the event system (https://github.com/facebook/react/issues/4751). The current event system has barely changed since its initial implementation in 2013. It is reused across React DOM and React Native, so it is unnecessarily abstract. Many of the polyfills it provides are unnecessary for modern browsers, and some of them create more issues than they solve. It also accounts for a significant portion of the React DOM bundle size. We don't have a very specific plan here, but we will probably fork the event system completely, and then see how minimal we can make it if we stick closer to what the DOM gives us. It's plausible that we'll get rid of synthetic events altogether. We should stop bubbling events like media events which don’t bubble in the DOM and don’t have a good reason to bubble. We want to retain some React-specific capabilities like bubbling through portals, but we will attempt to do this via simpler means (e.g. re-dispatching the event). Passive events will likely be a part of this.

We've tried this early in 2019, and a truly minimal event system did not work out very well in our internal testing. There was quite a bit of cross-browser normalization React is doing that is still useful for people with older browsers, or in more niche areas like rich text input editors using contentEditable. That said, as a part of our work on attaching events to roots, we've removed a lot of abstraction from the event system so it's easier to understand and improve in the future. As a part of React 17, we're removing "event pooling" which has caused a lot of confusion, and we're also no longer bubbling the onScroll event. We will likely follow with stopping bubbling of media events in React 18, bringing React behavior closer to the browser one. We did save some bytes with the new event system, but they were taken by new features we're working on, so it won't lead to a bundle size decrease overall.

  • classNameclass (https://github.com/facebook/react/issues/4331, see also https://github.com/facebook/react/issues/13525#issuecomment-417818906 below). This has been proposed countless times. We're already allowing passing class down to the DOM node in React 16. The confusion this is creating is not worth the syntax limitations it's trying to protect against. We wouldn't do this change by itself, but combined with everything else above it makes sense. Note we can’t just allow both without warnings because this makes it very difficult for a component ecosystem to handle. Each component would need to learn to handle both correctly, and there is a risk of them conflicting. Since many components process className (for example by appending to it), it’s too error-prone.

This was the most controversial part of the proposal. Since then, we released Hooks, which encourage writing function components. In function components, we generally suggest using destructuring for props, but you can't write { class, ... } because it would be a syntax error. So overall it's not clear that this is ergonomic enough to actually follow through with. I think it's plausible we'll revisit this in the future, or at least make class not warn and let people do what they want. But for now, we'll shelving this idea.

Hi, it's a great article!
Just wanted to know if there is any plan in pipeline to reduce React-DOM prod size? For mobile applications, it is still an overhead as the browser will be parsing 100+ KB of React-DOM JS and then other modules. Then app-specific JS.
For content-rich pages, it is causing greater Blocking and greater TTI.

Any Idea when can we see such changes?

@morevolk-latei In your measurements, how much time is spent parsing 100 KB of ReactDOM?

Was this page helpful?
0 / 5 - 0 ratings