Allow custom (nonstandard) attributes. #140

Open
steida opened this Issue Jun 30, 2013 · 111 comments

Projects

None yet
@steida
steida commented Jun 30, 2013

Various frameworks uses custom attributes. React could allow to extend default data- and aria- prefixes. Something like this:

React.registerCustomAttributePrefix('e-');
React.registerCustomAttributePrefix('ng-');
@syranide
Contributor

Cross-post from #1730

Have we considered something like an attrs-property? It would pass through any value as-is as an attribute for the DOM (same could be considered for a props-property, although it look kind of ambiguous).

I like it in the sense that it would work like data and aria is going to (soon), and it's use-case is actually very similar (i.e, use unsupported/unofficial attributes) so it kind of makes sense to me.

@Swivelgames

@syranide That's definitely an option to consider, imo (for whatever its worth :P ). It would certainly contribute to making this much less ambiguous or esoteric for new users.

@Swivelgames

@syranide, now, that being said, are you referring to using attrs- for standard properties? Or for React JS properties?

It might be a good idea (especially for users who aren't familiar with ReactJS) to use something like properties suffixed by react- for react-specific attributes. I'm not sure how big of a change we're talking about here, though, and this could be getting into a much larger discussion regarding how React JS essentially works at its core and how it introduces itself to developers, which is certainly not something I'm here trying instigate.

@syranide
Contributor

@Swivelgames Not sure if I understand, my intention with attrs-* (or rather attrs= to align with the coming data= and aria=) is that it would translate directly to attributes on the node. So <span attrs={{abc: 123}} /> would yield <span abc="123" />.

@Swivelgames

OH, I see what you're saying. I apologize, I misunderstood :)

@geelen
geelen commented Aug 9, 2014

This is also an issue for interop between React and Custom Elements - something like an attrs property, that allowed passing through arbitrary properties and diffing them as simple strings seems like it would work well here.

@janhancic
Contributor

Do you guys know when this feature is going to land in React?

I'm playing around with node-webkit, which supports a custom attribute nwdirectory on <input type="file"/>s, that allow you to select folders. But I need to find a workaround as React strips it ...

@syranide
Contributor

@janhancic this.ref.myInput.getDOMNode().setAttribute('nwdirectory') in componentDidMount is a work-around until there's movement on this.

@janhancic
Contributor

Yep, that's what I came up with :) Thanks.

@Aaronius
Aaronius commented Dec 2, 2014

I don't think the workaround @syranide outlined will work in all the necessary cases. For example, I don't believe you can do:

var MyButton = React.createClass({
        displayName: 'MyButton',
        componentDidMount: function() {
            this.getDOMNode().setAttribute('is', 'my-button');
        },
        render: function() {
            return React.createElement('button', $.extend({}, this.props));
        }
    });

And have it be properly upgraded as a "my-button" custom element. I assume because we're setting the is attribute after the element is created and placed in the document. I'd be happy to hear if there's another way to deal with this particular case.

@geelen
geelen commented Dec 2, 2014

Good find @Aaronius! I'd love for an official way to do this before the element is added into the DOM

@nfroidure

@syranide thanks for the trick.

As @Aaronius pointed out, it would be great to have something like a componentWillBeAttached(node:DOMNode) method in order to perform any action before its effective insertion in the DOM.

@nfroidure

Sadly, the method described above doesn't work server side. A componentWillBeAttached as i mentionned above won't work either server side.

Is it feasible to create a componentWillRender method that would allow to modify outputted HTML either on server and the browser and allow us to simply solve the legacy/custom Tags/Attributes problem with a simple Mixin ?

@nfroidure

Here is the solution i came accross SimpliField@8861a94.

It allowed me to inject custom attributes for my needs with this piece of code:

var HTMLDOMLegacyPropertyConfig = {
  isCustomAttribute: function(attributeName) {
    return -1 !== [
      'align', 'bgcolor', 'border'
     ].indexOf(attributeName);
  },
  Properties: {
    align: null,
    bgcolor: null,
    border: null
  },
  DOMAttributeNames: {
  },
  DOMPropertyNames: {
  }
};

var React = require('react');

// Allow custom/legacy attributes for mail templates
React.Injection.DOMProperty.injectDOMPropertyConfig(HTMLDOMLegacyPropertyConfig);

Any better way to inject this ? If no, any plan on exposing ReactInjection on the React main object ?

@robink
robink commented Dec 15, 2014

Just came across the same issue... The workaround mentioned by @syranide won't work on server side. The current situation prevents me from generating email HTML with legacy attributes like "align" "bgcolor".

@steida
steida commented Dec 15, 2014

On the server side, you can use very dirty string replacement based workaround. Use data-fokfokfok prefix, and it will be safe enough. Still, React should and has to allow custom attributes, because Polymer. @sebmarkbage ?

@robink
robink commented Dec 15, 2014

So you would need to do a workaround for client side and another one for server side. Not very neat... :( Too bad that HTMLDOMPropertyConfig is not easily configurable.

@sebmarkbage
Member

We want to move to a model where we render all attributes that you provide. Without a whitelist. There are a few concerns though.

It's a bit dangerous because we might need to change the meaning and signature of those attributes. E.g. as complex properties gets added to HTML, your code might break between versions. We prefer rich data types instead of strings.

We also have the issue of patterns like transferPropsTo used to transfer too many properties and people specified invalid HTML properties. These used to be silently ignored. We would need to provide a nice upgrade path for this case.

We could probably do it for web-components but not HTML or we could commit to always supporting string values and try to find an upgrade path for existing code.

@gaearon
Member
gaearon commented Dec 15, 2014

E.g. as complex properties gets added to HTML, your code might break between versions.

But isn't this true in web anyway (i.e. when not using React)?

IMO it's the expected behavior in the web that if HTML adds an attribute and I already use it, whether by accident or on purpose, something might break. Moreover it's not like attributes get added really often.

We also have the issue of patterns like transferPropsTo used to transfer too many properties and people specified invalid HTML properties.

Since transferPropsTo is deprecated, would this still be an issue?

@sebmarkbage
Member

@gaearon Yes, but properties are riskier. You're less likely to rely on a property (unless you patch prototypes) than an attribute. We try to model them as properties when possible.

@geelen
geelen commented Dec 15, 2014

I didn't follow this last comment @sebmarkbage, are you talking about more complex attribute values in custom elements, that can't be simply represented by strings? I'm not clear on how that would work, I'd have thought that all communication with the custom element needs to be done through the DOM, as strings?

Rather than rendering all attributes without a whitelist, maybe just adding a separate stringAttrs: { attr: val } object that can be used to explicitly declare a list of attributes to be treated as simple strings. That way simple attributes can be supported without breaking code, which is my use case and (from following this issue) seems like most of the others here?

@sebmarkbage
Member

For custom elements, we can only really support simple strings since we don't have any rich information about what they will be. For known HTML elements we can use rich data structures for style, classList, matrices for SVG attributes, etc. Maybe the solution is to simply use setAttribute if a string is provided when we also have rich property support. E.g. style would accept a string. Although that's a security risk so we might not allow that.

The nested stringAttrs property could work, but even then, it might not be great from a security perspective and not very elegant.

I think we can find a way to support all the things. My primary concern is the upgrade path. I have some ideas there.

We could replace existing uses of <div {...this.props} /> with a wrapper that only propagates the whitelist. E.g. <ConstrainedLegacyDiv {...this.props} />

@syranide
Contributor

@sebmarkbage You are definitely the authority on this, but to me it seems natural to otherwise simply add a dedicated prop for "setting attributes/properties with raw values on DOM nodes" (i.e. attrs={...}) which also comes with the understanding that whatever you provide it the attr gets set to.

It's probably not a very nice idea at all applied to custom elements, but then again, making the default implementation for all DOM elements non-whitelisted is definitely not nice either IMHO, especially as it conflates two very different behaviors without any hint as to which one you'll get.

Obviously, just custom elements could be "pass-through" and it kind of fits nicely with the fact that they too are uniquely named (apart from handful SVG nodes that conflict, unless they need their own namespace anyway?).

Anyway, just rambling here. :)

@sebmarkbage
Member

My rationale is that it is unlikely that a DOM property that accepts a string would have different behavior from the attribute. E.g. .setAttribute('class', str) has the same semantics as .className = str.

Except for the imperative quirks of what time they're mutated. Browsers have bugs or unintuitive behavior for when they update, and at which time you would want to invoke the setter. There's also internal state in the component that might update a property while leaving the attribute the same.

The point is that even if we added the special behavior for className later on, the behavior would be unaffected - except to fix unexpected life-cycle quirks. Having an attrs={...} escape makes it confusing how those differ from the other properties and they also don't get the bug fixes from the other properties.

@nfroidure

I think we should think about what custom tags/attributes will be in the future. With web components adoption, i think they will be heavily used.

So if having a deep knowledge of properties is useful for 'traditional' HTML, why would it be different for the future HTML ?

That said, why not use the property when she is exposed and set the attribute when not. Allowing to inject your own rules like i suggested above seems to be the most universal solution.

It also could allow to put HTML/SVG properties definition in another repo that could be updated at a quicker pace. It would also decrease the sources size if you only use SVG or HTML or none of them.

The only downside of it is that the injection API would be harder to change since users will rely on it.

@tehnomaag

I ran across this "custom" attributes issue too, but with trying to render (standard) SVG. So I thought I'll add my real world example to this issue.

I was trying to use masks like so:

render: function () {
return <svg>
  <defs>
    <mask id="my-mask">
      <rect .../>
    </mask>
  </defs>
  <g mask="url(#my-mask)">
    ...
  </g>
</svg>;
}

First problem is that to make the url(#...) syntax work you have to have xmlns:xlink="http://www.w3.org/1999/xlink" attribute set on <svg> tag. And that is not whitelisted attr. OK I can add that with setAttribute() call in a componentDidMount function if needed.

But also the mask attribute is not whitelisted by React, so I'd need to go and modify all those DOM nodes one by one as well if I had many masked items rendered there.

Hopefully this shows that there are other and "standard" attributes that React is missing support for.

@dandelany

+1 on some sort of solution to this - I think I prefer no attributes whitelist at all. There are a huge number of possible SVG attributes, many of which are currently unsupported. This makes it quite a pain to do anything beyond the most basic SVG rendering with React - which is a shame because I think SVG should be in React's wheelhouse. I've definitely run into the "mask" issue mentioned above before; this time though it's "markerWidth" and "markerHeight" that are causing problems for me.

@jstrimpel

Would it be possible to introduce the concept of a config to support customization like this? Seems like this could be expanded to solve the SVG attributes issues as well by making isCustomAttribute for the SVG attributes config match all strings.

React.config({
  HTMLDOMPropertyConfig: {
    isCustomAttribute: RegExp.prototype.test.bind(/^(data|aria|lazo)-[a-z_][a-z\d_.\-]*$/),
    Properties: {
      'some-other-prop': null
    }
});

// would trigger ReactInjection.DOMProperty.injectDOMPropertyConfig(HTMLDOMPropertyConfig);
@jimfb
Contributor
jimfb commented Apr 15, 2015

@jstrimpel We want to avoid adding "configs" because they make things more complicated for users (more api surface area, more things that could go wrong / introduce component incompatibilities) and they make it more difficult for us (more permutations of conditions need to be tested). We know we want to get rid of the attributes whitelist anyway, so it makes no sense to introduce a "config" and then take it away in the subsequent release.

@jimfb jimfb closed this Apr 15, 2015
@jimfb
Contributor
jimfb commented Apr 15, 2015

Oops, didn't mean to close.

@jimfb jimfb reopened this Apr 15, 2015
@jimfb jimfb closed this Apr 15, 2015
@jimfb jimfb reopened this Apr 15, 2015
@jstrimpel

@jsfb Thanks. +1 for removing the whitelist. Is there an ETA on that?

@geelen
geelen commented Apr 15, 2015

Awesome, removing the whitelist will fix my use case perfectly. Very interested to see what SVG wizardry you're cooking up @tehnomaag & @dandelany, I hadn't considered using React for SVG but it could be pretty cool!

@jhicken
Contributor
jhicken commented Apr 23, 2015

+1 For removing the whitelist.

@Aaronius

I appreciate the removal of the tag name whitelist. That has allowed us to support many custom elements however it has not allowed us to support custom elements that extend from native elements which require the is attribute as described in my previous comment. A resolution on this would be super-duper-appreciated.

@spicyj
Member
spicyj commented Apr 23, 2015

@Aaronius It's something we want to do. Until then, I'm happy to take a PR adding "is".

@jhicken
Contributor
jhicken commented Apr 23, 2015

@spicyj I would totally do a PR for something small like adding is but I don't think it would solve the entire custom element problem. So its more than just the is attribute. For example if you use the is attribute to extend an element and your extension uses a custom attribute named numberofplops then numberofplops would be removed and your custom element may fail to behave properly.

I can get around it for now using @nfroidure 's method but it make me really sad to do that. So any info about priority to remove the whitelist would be rad.

@jimfb
Contributor
jimfb commented Apr 23, 2015

@jhicken Currently we skip the whitelisting check if the component name contains a dash. To enable component inheritance, you would need to also skip the whitelist check if the component contained an is attribute; that would allow you to have arbitrary properties on your custom element.

@spicyj
Member
spicyj commented Apr 23, 2015
@jhicken
Contributor
jhicken commented Apr 27, 2015

I just threw a commit together that does what @jsfb suggested. Have a peak let me know your thoughts.

@Gozala
Gozala commented May 12, 2015

This would resolve an issue I've being struggling with for a while (see #2746 for details)

@jhicken
Contributor
jhicken commented Jun 10, 2015

So the now this stuff works on master I found an issue where if your using a custom component react does not translate className to class.

EX:
<awesome-sauce className="fluffy" />

becomes

<awesome-sauce classname="fluffy" />

instead of

<awesome-sauce class="fluffy" />

What file makes this translation?

@ljharb
Contributor
ljharb commented Jun 20, 2015

This issue has been open for about 2 years - is there any chance of being able to use any attributes I wish in jsx for a general case?

@sebmarkbage
Member

@ljharb For custom elements, I.e. tags with dashes in them, yes. That has landed in master. For other tags, no. What is the use case? Is it an attribute with a dash in it? We might be able to support that.

@jhicken Custom elements uses "attributes" where as HTML nodes uses "properties". That's why they have different names. We don't intend to change that. It is just a consequence of custom elements relying heavily on attributes but the proper way to interact with DOM is through properties.

@ljharb
Contributor
ljharb commented Jun 20, 2015

No, no dashes. See https://help.pinterest.com/en/articles/prevent-pinning-your-site - the "nopin" attribute is what Pinterest requires to exempt an image from showing up in the "pin" dialog. This is nonstandard (shame on them), but has no dashes, and isn't something I can control.

How can I use this in jsx without creating a custom element?

@jhicken
Contributor
jhicken commented Jun 22, 2015

@ljharb You could use the the is attribute to force react to ignore the whitelist. The is attribute ignoring is also new in master. I think thats a really hacky way to use the is attribute but It would work. Just remember that if you put a class on the element with the is attribute you have to use class not className.

Like this.
<img src="foo.jpg" is nopin="nopin" class="dontTazeMeBro" />

@sebmarkbage Thats too bad... I don't know enough about the separation of how react deals with attributes vs properties under the covers to really present a real solution to this problem. However I do see this as an api issue. I think as custom components become more popular. This is going to become really annoying to have a goofy mix of class and className attributes in your jsx. (I say this because I'm working on a project that makes heavy use of custom components) So this may be a naive question but, would it be possible when - is in element name or is attribute exist.. we convert the elements html attributes to react properties?

@sebmarkbage
Member

@jhicken We don't know what className might mean for a custom component. That might be the name of a legit attribute so if we were to convert it, we would now do some weird magic work that breaks that API. Unlike properties, attributes can only be strings so not all APIs can safely be converted. Additionally, you'll notice that event listeners won't work for these. Same for styles.

It is the opinion of the designers of web components and the spec that attributes is a mistake and should not be used directly. It is a serialized form. It shouldn't be used directly for anything other than serialized HTML, and then properties should take over. However, this is not really how most custom elements are consumed. The public APIs right now are mostly attributes. If properties are made available they're typically imperative methods that doesn't translate well to React's declarative API. We're forced to use attributes since it is the only thing that is possible for custom elements.

For consistency, the only solution would be to revert what we do for normal HTML elements and have them apply attributes instead of properties. That is more intuitive but strictly inferior since it doesn't allow us to properly model things like classList, transform, defaultValue vs. value, checked etc. These are not just attribute strings, they come with richer data structures or richer behavior differences than the serialized form allows for.

More over, it is our opinion (and the opinion of the React, Ember and Angular teams) that custom elements (Web Components) is a flawed model for composition and it won't be treated as first-class in either framework. There are too many compromises to make that work flawlessly.

We don't want to compromise the primary HTML API to support the custom elements edge case. So unfortunately, that's how we end up with this subpar API.

The recommended solution is to wrap your Web Components in a React component that can provide a richer API and use refs to interact with events / properties / methods. As part of that you can always translate the names as you see fit <my-component class={this.props.className} />.

@ljharb We would like to support a non-whitelist solution but not quite sure how to tackle it safely right now. As a workaround you can always get a ref on an element and call setAttribute('nopin', '') on it yourself. Ofc, this is annoying to do many times but best practice is to build small reusable components that you compose, if you do that then you should only need to use this hack once.

Btw, I will address these issues in my React Europe talk on July 2nd.

@dantman
Contributor
dantman commented Jun 22, 2015

@sebmarkbage

@ljharb We would like to support a non-whitelist solution but not quite sure how to tackle it safely right now. As a workaround you can always get a ref on an element and call setAttribute('nopin', '') on it yourself. Ofc, this is annoying to do many times but best practice is to build small reusable components that you compose, if you do that then you should only need to use this hack once.

This probably won't work in this case, as Pintrest is probably not executing client side JS and instead simply statically analyzing <img> tags for a nopin attribute.

@sebmarkbage
Member

Oh, this is for server-side rendering? Yea, that sucks. Maybe Pinterest should be nicer community members and use the data- namespace like it was intended. :(

@spicyj
Member
spicyj commented Jun 22, 2015

Oh, I assumed this was for the browser extension that adds "Pin It" buttons to everything.

@dantman
Contributor
dantman commented Jun 22, 2015

Oh, this is for server-side rendering? Yea, that sucks. Maybe Pinterest should be nicer community members and use the data- namespace like it was intended. :(

Yeah, for that use case. Though data attributes also sound wrong. This would be easier if <img> had something like rel so rel="nopin" would be viable.

@spicyj
Member
spicyj commented Jun 22, 2015

data-pin-nopin="true": pinterest/widgets#29

@sebmarkbage
Member

Nice @spicyj! Problem solved. I figured I should've checked for alternatives but I'm sick in bed and also lazy.

@Gozala
Gozala commented Jun 24, 2015

@sebmarkbage @spicyj this maybe solving this specific instance but it is still a very painful issue for some of us. We are basically forced to do a horrible hacks to workaround lack of general attribute support. I have wrote about this in #2746 and also explained workardounds we employ.

I would very much like for this issue to be resloved maybe for the very least react can be made configurable so users could extend the whitelist.

@spicyj
Member
spicyj commented Jun 24, 2015

@Gozala Yes, we understand. That's why this issue is still open.

@pencilcheck

Using angular material with react and attributes such as "layout" that are introduced in angular material doesn't work. How do I whitelisted those attributes for the moment? https://material.angularjs.org/latest/#/layout/container

@chromakode

Based on @nfroidure's approach above, for server-side rendering (e.g. rendering archaic table attributes in HTML emails), here's a workaround I'm experimenting with using in the meantime:

var DOMProperty = require('react/lib/ReactInjection').DOMProperty
DOMProperty.injectDOMPropertyConfig({
  Properties: {
    'valign': DOMProperty.MUST_USE_ATTRIBUTE,
  }
})
@terinjokes

With 0.14 splitting react-dom from react, could an alternative module be created to allow these archaic HTML attributes (react-dom-emails or something of the like)?

@IL55
IL55 commented Jul 31, 2015

I just want to notify that in React 0.13.3, still hasn't support for svg "mask" attribute. Please don't mix up with mask tag, i.e. next is allowed:

      <defs>
           <mask id="myMask"> .... </mask>
      </defs>

but using mask is not possible (react 0.13.3 removes that attribute)

    <g mask="url(#myMask)"> ... </g>

Here is jsfiddle example:
https://jsfiddle.net/kbcpLyax/3/

More details could be found in next comment:
#140 (comment)

@yaycmyk
Contributor
yaycmyk commented Jul 31, 2015

@IL55 i'd open a separate ticket for it

@Deepblue129

React 13.3 strips my Animate tag.

From:

<animate attributeName="stop-color" dur="24s" values="rgba(0,0,12,0);rgba(25,22,33,.3);rgba(32,32,44,.8);rgb(58,58,82);rgb(81,81,117);rgb(138,118,171);rgb(205,130,160);rgb(234,176,209);rgb(235,178,177);rgb(177,181,234);rgb(148,223,255);rgb(103,209,251);rgb(56,163,209);rgb(36,111,168);rgb(30,82,142);rgb(91,121,131);rgb(157,166,113);rgb(233,206,93);rgb(178,99,57);rgb(47,17,7);rgb(36,14,3);rgb(47,17,7);rgba(75,29,6,.4);rgba(21,8,0,0);rgba(0,0,12,0)" repeatCount="indefinite"></animate>

To:

<animate data-reactid=".0.0.1.$=10:0.$=10:0.$=10:0.$=11:0"></animate>

Any ideas on how I can overcome this?

@suryagaddipati

@Deepblue129 you can do ref.getDomNode().setAttribute(... in component lifecycle method.

@despairblue

This also makes it impossible to use react to generate x3d scenes like

<transform>
  <inline url={url} namespace={url}/>
</transform>

and having these rendered using x3dom or later natively by the browser

Most attributes will be stripped from the rendered content, like translations or rotations, or even url for the inline node above.

@almirfilho

It doesn't allow tabindex attribute too. So it's impossible to follow the W3C WCAG Spec SCR29 to enhance accessibility in non focusable elements by default, like a simple <div>.

@syranide
Contributor
syranide commented Sep 4, 2015

@almirfilho tabIndex and it'll work just fine.

@almirfilho

@syranide thanks for the tip!

@oliger oliger referenced this issue in hull-ships/hull-login Sep 7, 2015
@oliger oliger center legend in fieldset in firefox
Had to set the attribute on the DOM element manualy because React strip the
align attribute...
134c609
@eyworldwide

@chromakode thx! it works in my project...

@chrissheppard41

Hi,

Might be worth taking a look at supporting googles Rich snippet https://developers.google.com/structured-data/rich-snippets/?hl=en

Just a possible idea

@dantman
Contributor
dantman commented Jan 22, 2016

@chrissheppard41

Might be worth taking a look at supporting googles Rich snippet https://developers.google.com/structured-data/rich-snippets/?hl=en

Microdata, RDFa, and script tags are already supported.

@chrissheppard41

@dantman

Yeah I read this: https://facebook.github.io/react/docs/tags-and-attributes.html

But when I embed say "itemscope" or any others like so:

    render() {
        return (
            <div ref="parent" className="review_feed" itemscope="" itemtype="http://schema.org/Product">
                ...Something here
            </div>
        );
    }

It prints out:

<div class="review_feed" data-reactid=".0">
    ...Something here
</div>

It's not applying to the dom

This is likely to my misunderstanding but some clarity would be lovely. Thanks.

@dantman
Contributor
dantman commented Jan 22, 2016

@chrissheppard41 That's because React-JSX is not HTML. You converted the html class to React's className but didn't do the same for the rest of the attributes.

You need to convert the HTML examples to React-JSX:

<div class="review_feed" itemscope="" itemtype="http://schema.org/Product">
  ...Something here
</div>

Becomes:

<div itemType="http://schema.org/Product" itemScope className="review_feed">
  ...Something here
</div>
@chrissheppard41

Ah, I'm sorry, that worked, Thank you.

@monolithed

I really don't understand why some library should restrict my application architecture?

@antitoxic

@spicyj @sebmarkbage @syranide

So far, from what I've read, this looks like a real problem, not just a feature perk.

Is there a downside for React, before an ideal solution is provided, to supply some intermediate one?

Like:

<div thirdpary-attr='"i need this" allowAttrs="thirdpary-attr">
<!-- or previously defined -->
<div thirdpary-attr='"i need this" allowAttrs={this.allowedAttrs}/>
<!-- or -->
<div thirdpary-attr='"i need this" safeAttrs="thirdpary-attr">

I understand that the above is not the wisest thing to do with an API (introduce a temporary interface) but consider the issue is 2 years old. Even the best APIs I know change in that time.

If however, you completely disagree, could React documentation be changed so that it includes a workaround? That will be super-useful.

@antitoxic

PS: Aside from use-cases given above (+ others things like #3809) I'm using attributes for something that people can call me heretic - simply for clear way of styling (i.e. .myCSSComponentClass[size="big"]) . Combining attributes and their values has proven to be way more readable than just throwing classes in and it's DRY-er than 'classnames({..})' which simply repeats properties most of the time.

For that purpose I'm using a simple ES7 decorator.

decorators.js:

import ReactDOM from 'react-dom';

export function customAttributes(...attrs) {
    return function(component) {
        var originalComponentDidMount = component.prototype.componentDidMount;
        component.prototype.componentDidMount = function() {
            if (originalComponentDidMount) originalComponentDidMount.apply(this, arguments);
            var rootDom = ReactDOM.findDOMNode(this);
            attrs.map(attr => {
                if (!this.props.hasOwnProperty(attr)) return;
                if (this.props[attr] == false && rootDom.hasAttribute(attr)) {
                    rootDom.removeAttribute(attr);
                    return;
                }
                rootDom.setAttribute(attr, this.props[attr])
            });
        };
        return component;
    }
}

button.js:

import React from 'react';
import {customAttributes} from 'decorators';

@customAttributes('color', 'size')
export class Button extends React.Component {
...
}

To me ot looks like a clean approach. Am I doing something terribly akward? Is this a reccomended approach?

@ajfarkas
ajfarkas commented Feb 4, 2016

Looking forward to this fix. Would like to use SVG Icons, but dangerously setting xlink:href is a bummer.

@spicyj
Member
spicyj commented Feb 4, 2016

@ajfarkas xlinkHref={...} works already in 0.14.

@ajfarkas
ajfarkas commented Feb 4, 2016

true, but <use> tags don't.

I appreciate the attempt at giving people a heads up, but this is producing a major headache, since I have to change all of my SVG code, then will have to change it back once this gets updated.

@spicyj
Member
spicyj commented Feb 4, 2016

<use> tags (and all SVG tags and attributes) should work in master and will work in v15.

What do you mean about changing your code?

@tnrich
tnrich commented Feb 19, 2016

Hey there,
Props on making react, its awesome! Pun intended :)

I'm trying to use the "inert" html attribute to disable a div (and polyfilling it so it works on all browsers), but react doesn't seem to be letting it through. Is there any way to get this to work currently?

Thanks!

@gaearon
Member
gaearon commented Feb 19, 2016

@tnrich

For not yet supported attributes, you can put ref on the element and call setAttribute() manually.

For example:

return <div ref={node => node && node.setAttribute('inert', '')} />
@tnrich
tnrich commented Feb 19, 2016

Awesome thanks @gaeron !

On Thu, Feb 18, 2016, 6:00 PM Dan Abramov notifications@github.com wrote:

@tnrich https://github.com/tnrich

For not yet supported attributes, you can put ref on the element and call
setAttribute() manually.

For example:

return <div ref={node => node && node.setAttribute('inert', '')} />


Reply to this email directly or view it on GitHub
#140 (comment).

@dantman
Contributor
dantman commented Feb 19, 2016

@tnrich inert was dropped from the standard drafts some time ago; it's not a pending standard.

@tnrich
tnrich commented Feb 19, 2016

@dantman, that may be so, but, is there a reason not to use it with a polyfill (kind of a misnomer in this case), given the listed caveats?

Here's the npm module I'm using: https://github.com/GoogleChrome/inert-polyfill

Sorry to stray off topic everyone..

@fritx
fritx commented May 6, 2016 edited

If you want to use a custom attribute, you should prefix it with data-.

But seems nwjs only recognizes nwsaveas, nwworkingdir (cannot have extra prefix), etc.

// adding data- prefix would break
<input type="file" nwsaveas="filename.txt" nwworkingdir="C:\\Windows" />

What could I do with this?
Thanks!

@jayphelps
Contributor
@fritx
fritx commented May 6, 2016 edited

@jayphelps thanks for pointing out the comment (there were too many to figure out)

until there's movement on this.

So there is still some movement on this?

I had tried that workaround before and it did work util later. But I've just found another issue nwjs/nw.js#3372 (comment)

Thanks to that buddy's great effort. I guess it's some problem of nwjs itself.

@fritx
fritx commented May 6, 2016 edited

I still wonder is a.setAttribute('xxx') entirely equivalent to <a xxx> stuff, or is there still some tiny difference between the two definition?

Btw, git-blame-someone-else is awesome. I just plan to fake some funny commit in my own prj 😄 @janhancic

@jayphelps
Contributor
jayphelps commented May 6, 2016 edited

@fritx using setAttribute inside componentDidMount is different than using React's JSX/virtual DOM in the sense that it happens after the element has already been inserted into the DOM. Depending on how that library works, that might impact things.

I find that often, when you decide it's worth it to integrate a library that is incompatible with React, it's best to just create a wrapper React Component and then just create the real nodes/use plugins and insert them inside componentDidMount, just like you'd do with regular JavaScript/jQuery; but remember to clean them up in componentWillUnmount. This isn't ideal, but I personally take a "reality isn't always ideal" approach to getting work done.

Also--I don't personally have knowledge of progress on react supporting arbitrary attributes, other than what's been discussed here.

@fritx
fritx commented May 9, 2016 edited

Btw, git-blame-someone-else is awesome.

As a bash beginner, I had to do kind of research, until I eventually created git-green-someone-else, which was heavily inspired by git-blame-someone-else.

Hope any guy will enjoy it!
And if I'm missing something, let me know.

PS: I'm fond of taking friends --force to my project so it looks active LOL..

@poscar
poscar commented May 18, 2016

Another use for this is for using React to perform server side rendering of Google AMP pages. Google AMP requires a <html ⚡></html> or <html amp></html>. More info here.

Currently react removes the amp attributes.

Thoughts?

@dantman
Contributor
dantman commented May 18, 2016

I think the ReactInjection.DOMProperty interface is really nice. In the future if that interface was polished and made into a public interface when the devs are happy with its stability; I think it would make for a very good solution to this issue.

The range of nonstandard properties with reason to be used in react is fairly narrow. If we had that interface, then we could just create libraries that would inject support for nonstandard properties and attributes into react.

A react-nw library could inject nw* attributes; a react-amp library could inject amp attributes; and a react-angular library could not just support ng-* attributes, but perhaps support them as actual properties such as ngModel. And these kinds of libraries could do more than just whitelisting attributes, they could better integrate React with the library they're tied to.

@sebmarkbage
Member

What if there's both react-angular and react-ng libraries both integrating using the same attributes?

@dantman
Contributor
dantman commented May 18, 2016

@sebmarkbage Then they'd be conflicting libraries and you could only use one of them. Just like if you tried to use Angular twice or another library that used ng-* attributes in the dom; or if you used two React components that defined and used a string type context parameter named foo for two different purposes.

@poscar
poscar commented May 18, 2016 edited

Sadly the following snippet does not work for me:

const DOMProperty = require('react/lib/ReactInjection').DOMProperty;
DOMProperty.injectDOMPropertyConfig({
  Properties: {
    amp: DOMProperty.MUST_USE_ATTRIBUTE
  },
  isCustomAttribute: (attributeName) => {
    return attributeName === 'amp';
  }
});

Any ideas why?

I'll have to resort to doing string replacement, if I can't get React to allow this attribute. :(

UPDATE (05/2016):
Turns out the workaround wasn't working because it wasn't being applied to the React module actually loaded by the dependency of my project. I managed to get the workaround above to work.

@sebmarkbage
Member

The server renderer not having an alternative way to fix this particular problem is unfortunate. We should fix that in isolation. Spawned a new issue. #6798

@SunnyGurnani

Is there any update on this?

@benjdlambert
Member

@jayphelps is 100% spot on.

I had a real pain trying to work out a shocker of a bug recently.
With the addition of the new playsinline to iOS10, if you put it in componentDidMount and the src attribute is already on the element from the render() it will not play inline.

I guess its kind of a bug from Safari point of view, but this is just one use case.

There will be other cases where adding elements after the element has already been inserted into the DOM which cause issues.

I fixed this, by using the is property on the element, which removes validation of props.

/blam

@jayphelps
Contributor

@benjdlambert I think you meant someone else, my only comment was linking to someone elses comment. 😄

@benjdlambert
Member

@jayphelps I meant the comment after your link to the comment 👍

@nhunzaker
Contributor

FWIW, I've been maintaining (sort of) a PR to remove the attribute whitelist:

#7311

I don't speak for the React team but, hopefully, if the decision is made to support custom attributes by eliminating the whitelist, this handles much of the legwork.

@antitoxic

In continuation of what @gaearon said in Feb this year, if anyone wants to make custom attributes a bit more tidy to write, you can do it with a babel plugin (or other AST-based transformer if you're not using babel).

Here's a quick working example (you can change the attrs to what you find appropriate):

// your view
function MyComponent({size, density, href}) {
    return <a href={href} attrs={{size, density}}>Go</a>
}
// the babel plugin
var template = require('babel-template');

var refBody = template(`
    var attrs = ATTRS_MAP;
    Object.keys(attrs).forEach(function(k) {
      n.setAttribute(k, attrs[k])
    })
`);

module.exports = function ({types: t}) {
  var visitor = {
    JSXAttribute(path) {
      if (path.node.name.name != 'attrs') return;
      path.replaceWith(
        t.JSXAttribute(
          t.JSXIdentifier('ref'),
          t.JSXExpressionContainer(
            t.functionExpression(
              null,
              [t.Identifier('n')],
              t.blockStatement(refBody({
                ATTRS_MAP: path.node.value.expression,
              }))
            )
          )
        )
      );
    }
  };

  return {
    inherits: require("babel-plugin-syntax-jsx"),
    visitor: visitor
  };
};

Which will generate the required ref logic and output:

function MyComponent(_ref) {
        var size = _ref.size;
        var density = _ref.density;
        var href= _ref.href;

        return React.createElement(
            'a',
            { href: href, ref: function ref(n) {
                    var attrs = { size: size, density: density };
                    Object.keys(attrs).forEach(function (k) {
                        n.setAttribute(k, attrs[k]);
                    });
                }
            },
            'Go'
        );
    };
@Gozala
Gozala commented Nov 7, 2016

Problem with a ref based solution is that ref is called after element is already in the document tree, unfortunately sometimes you need to set attributes before element is in the document tree or behavior is going to be different.

@vizath
vizath commented Nov 18, 2016

Note that since 15.4, ReactInjection is not part of React anymore, but of ReactDOM

const DOMProperty = require('react-dom/lib/ReactInjection').DOMProperty;
@alivedise

It's about 2017 and this is still opened. I understand that you don't think property and attribute is the same thing, but why not just treat all custom attribute on native element as legal and treat all unknown property on react element as 'property'?

If you can't catch up all the needs of new attributes on time, I'm afraid you have to open it to the developers. It makes me feel React is telling us using custom attribute is guilty. But why? Is Web standards telling us don't use that? It's indeed a real requirement.

@AntonioRedondo
AntonioRedondo commented Dec 7, 2016 edited

There are many valid use cases above described where it should be possible to add custom attributes. I've came across with another one: to attach onReadyStateChange to <iframes> so that I can track when the <iframe> starts loading the embedded page. You cannot do this with element.addAttribute() since the element isn't accessible when it starts loading after the src attribute is parsed. EDIT: the onreadystatechange event is only fired for document and it cannot be attached to any HTML element.

@sebmarkbage
Member

We will be removing the whitelist but we need to give people time to clean up their existing codebases with the warning we added. So this will wait for a future major release.

I understand the frustration but would you agree that this slight inconvenience is worth keeping the ecosystem from diverging?

@Swivelgames

@sebmarkbage You're talking about a "slight inconvenience" that's hindering many developers in development, forcing work-arounds and hacks for nearly three and a half years now. Suffice to say that the divergence from standards and best practices in order to ensure development can continue on those projects is not entirely an acceptable side-effect to something so imperative. Would you agree?

The few ~40 who have contributed to this thread, among the many many others, will be eagerly waiting for this future major release.

@gaearon
Member
gaearon commented Dec 9, 2016

@Swivelgames

I will refer you to the section on “stability” in our design principles. We know people want this change. This is why we added the deprecation warning for spreading non-DOM props. It generated a ton of negative feedback because people relied on spreading props but we know this is important to people in this thread, and deprecating is a necessary step before we can introduce breakage.

Deprecation only happened in React 15.2.0 so it’s only been deployed for half a year. I hope you agree this is a reasonable timeframe to wait for projects to update, including open source projects many people rely on (for example, React Bootstrap and similar libraries).

@nfroidure

Better late then never, i guess. Sadly the issue was raised 3 years ago when a deprecation cycle would not have been necessary if feedback had been taken in count immediately. Not blaming anyone but just giving input on why i decided not to use React in production projects since i noticed no move were done on this issue.

@gaearon
Member
gaearon commented Dec 9, 2016

It would indeed be great if all issues could be taken into account immediately. However this is just not how software is developed. At the time, there were always more pressing issues. We try to do our best to prioritize work on what is likely to help most people, and while this particular request has a lot of support, so do many other things we are and have been working on.

I appreciate you sharing your concerns but let’s keep this thread on topic. Yes, it’s a change we wanted to make for a while, and yes, it took us a while to get to add the warning. If you open any popular issue, you will find comments saying that particular issue is the most urgent one.

I’m sorry this particular change prevented you from using React but I hope that you found an alternative that better suits your needs in the meantime. I also hope you’ll reconsider after we make this change.

Cheers!

@cytGitHub

@steida @Swivelgames @janhancic
<video webkit-playsinline=true x-webkit-airplay=true x5-video-player-type = "h5" playsinline>
how to use Video Attribute in React JSX ??

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment