React Unknown DOM Props: Why Component Props Reach the HTML

Image by Marija Zaric.

In Brief

React 15 warned about unknown DOM props and usually removed them. React 16 passes most unknown attributes through to the rendered HTML. Consume props which belong to your component before spreading the remaining DOM props, and use data-* or aria-* only when the attribute genuinely belongs on the element.

After moving a project to React 16, you might notice attributes in the generated HTML which were never meant to be there. A prop such as layout or isCompact makes sense to the component which received it, but it makes considerably less sense on the final div.

This isn't React losing track of the prop. It's usually the result of passing an entire props object to a native element, combined with a deliberate change in React 16.


The Behaviour Changed in React 16

React has always let components receive whatever props their interfaces need. The question is what happens when those props are passed onwards to a native element such as div, button, or input.

In React 15, the renderer maintained a list of recognised DOM properties. An unknown property generated a warning in development and was generally omitted from the HTML. This caught mistakes, but it also meant React had to keep a sizeable attribute list and made new browser attributes or custom attributes awkward to use.

React 16 changed that behaviour. Most unknown attributes are now included in the DOM rather than removed. The release notes describe this as part of React 16's support for custom DOM attributes, and React 16.0 was released on 26 September 2017.

This component demonstrates the difference:

function Panel(props) {  return <div {...props} />;}<Panel layout="compact">Account details</Panel>

With React 15, layout would provoke the unknownproperty warning and would not normally appear in the output. With React 16, the browser receives an element equivalent to this:

<div layout="compact">Account details</div>

That may be harmless, but it is still a component implementation detail leaking into the document.


A JSX Spread Does Not Know Your Intent

There's nothing special about the name props. It is an object, and the JSX spread syntax passes each of its properties onwards. This is convenient for wrappers which accept familiar DOM attributes:

function Panel(props) {  return <div className="panel" {...props} />;}

Now a caller can supply id, title, tabIndex, or an event handler without the Panel component listing each one. The same spread also forwards layout, tone, isCompact, and every other setting which was only intended to help Panel decide how to render.

React can't reliably infer which of those names are private to our component. Supporting unknown attributes is useful precisely because the browser platform and other tools can introduce attributes which React does not know in advance. The component has to make that choice instead.


Consume Component Props Before Forwarding the Rest

The direct fix is to take the componentonly values out of the object, then spread what remains. This is also the approach shown by React's unknownprop warning guidance:

function Panel(props) {  const { layout, className, children, ...domProps } = props;  const layoutClassName = 'panel panel--' + layout;  const panelClassName = className    ? layoutClassName + ' ' + className    : layoutClassName;  return (    <div {...domProps} className={panelClassName}>      {children}    </div>  );}

layout is read by the component and used to build its class name. className and children are also handled deliberately. Only domProps is forwarded to the div.

Object rest syntax needs to be supported by the project's JavaScript transform, as it commonly is in React build setups. If it is not, the same separation can be made by constructing a new object and deleting the componentonly keys before spreading it. What matters is that we do not mutate the original props object.

The order of the spread is worth noticing as well. In the example above, className appears after {...domProps}, so a value inside domProps cannot overwrite the class name we have just constructed. Spread order determines which duplicate prop wins.


Custom Attributes are Not Necessarily Mistakes

React 16's change exists for a good reason. A custom attribute may be understood by another script, used by a browser feature React has not yet added to its property list, or needed by a custom element.

For applicationspecific data, HTML already provides the data-* convention:

<div data-layout="compact">Account details</div>

Unlike a private component prop, data-layout says that the value is intentionally part of the document. It can then be read through the HTML dataset API where browser support allows.

The same principle applies to aria-* attributes, but they are not generalpurpose data storage. They describe accessibility semantics and state. React 15 already passed data-* and aria-* attributes through, and React 16 continues to do so, including development warnings for misspelled ARIA attribute names.

There are also deliberate exceptions to React 16's passthrough behaviour. Attribute names beginning with on are not passed through as unknown attributes because doing so could create a security problem. Functions supplied to ordinary nonevent attributes are warned about and ignored rather than converted into useful HTML.


Treat the DOM as the Public Result

The rendered element is where component details become browser markup. Props such as id, title, and valid event handlers may belong there. Props which select a visual variant, carry fetched records, or control internal branching usually do not.

React 15's recognisedproperty list could hide an overly broad props spread by stripping the result. React 16 makes that spread more visible. The dependable fix is not another list inside React, but a component which makes a clear distinction between the props it consumes and the attributes it deliberately forwards.

Postscript

Aug 2026: This article forms part of an archive restored from a previous version of my website. Its original publication date is accurate. During the restoration, I reviewed and updated it where appropriate for formatting, imagery, broken links, code correctness, and current internal references, whilst preserving the original technical context and intent. React still advises consuming parentonly props before a value reaches a native element, although current warnings and attribute handling have continued to evolve. Styling libraries have also added their own filtering conventions; Understanding Transient Props in styled-components covers one later example. Explicit prop filtering remains the more general rule.

Looking for technical direction?

I support teams that need senior judgement on React, Next.js, headless CMS architecture, performance, migrations, and technical SEO.