react: className prop not working with custom DOM elements in 0.14.0-rc1

The className prop does not appear to get mapped correctly when applied to custom DOM elements in 0.14.0-rc1.

JSFiddle: https://jsfiddle.net/hellosmithy/5pdujnfq/1/

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 3
  • Comments: 61 (19 by maintainers)

Commits related to this issue

Most upvoted comments

It works if you set class instead.

cc @jimfb perhaps? (#3067 is related I assume)

@jimfb what we’re already seeing is that the change between standard DOM elements and custom element behaviour is counter intuitive and thats the point being made. You’d assume currently that using className would output class because thats what happens everywhere else. Suddenly the behaviour changes because of a dash in the element name which is arbitrary. You’re assuming that the use of a dash means I want to create a web component that is going to have special handling of attributes. It’s currently counter intuitive which is why it keeps going around in circles. It seems like the extensive whitelisting is a result of the behaviour change added to custom elements where all props are mapped.

It’s clear we all care about this stuff because react is probably the coolest framework around and we use it every day. So yeah people are going to get passionate about it because its so important. Whats important especially to us is the use of custom elements as standard html elements using the current 0.13 behaviour that had no prejudice between elements. All were equal. What we’re saying is that the non-equality proposed should be done using a setAttr rather than the current behaviour forking which seems wrong.

I’m suggesting there should be a discussion on the best way to do it, escaping may not be it.

One way would be an escape pattern convention e.g. myPropEscaped (just a straw man example). But I’m also wondering, if you want to create web components that are outside of React’s scope surely that is a case for using dangerouslySetInnerHTML, I would have thought that the default behaviour should be interoperability otherwise there’s no way to apply React attributes such as className to custom elements, which seems an odd and inconsistent restriction. On the other hand there are other ways to pass through web component attributes in those cases where you might want to, and in those cases surely you should be explicitly saying “I don’t want React to do the default behaviour in this instance”, rather than it just automatically making that assumption for you.

Looking at the JSX code:

<div className="myClass" />
<div is="custom-elem" className="myClass" />
<custom-elem className="myClass" />

It seems right to expect those to behave consistently.

I agree there’s a distinction between custom elements and web components. Custom elements are one of the building blocks of web components but they are a spec in their own right. But there’s a use case for creating semantically meaningful custom elements which don’t extend default DOM behaviour.

Wouldn’t a more consistent approach be to treat all elements the same within React but allow either users to escape attributes somehow or alternatively they have the option to dangerouslySetInnerHTML if they are indeed using web components that need to operate outside the React scope?

@syranide is correct, there is a difference between DOM nodes and webcomponents (which are much more like React components). For standard DOM nodes, we know a priori that there is no className attribute, so we can commandeer the namespace for our own purposes. For web components, there is no such guarantee, so we can’t steal the namespace. In fact, web component authors may prefer className over class for the same reasons we stole it for standard dom components, and to remain compatible, we need to pass it through unmolested.

Another way of thinking about this: For standard DOM nodes, we conceptually have a react-component defined for each node, that happens to take className, just as we happen to take defaultValue for uncontrolled form elements and happen to use camel case for attributes. We happen to follow some consistent naming conventions in our framework-provided components. The fact that we do most of our markup transformations at the framework level instead of inside react-component definitions is an implementation detail. Web component authors are free to follow those same conventions (or not), but we need to pass them through without transformations or we’ll confuse the web components.

TLDR: Custom elements DO work in React. You must use class instead of className because the custom element spec requires that we allow users to specify a className attribute and we need to preserve that functionality for custom elements.