State of things We all know this simple rule. Use instead of if you write JSX. className class const ExampleReactComponent = () => { return <div className="foo">Example React component</div> } us about this convention straightaway. And it goes even further, you need to specify all HTML attributes in . React docs warns camelCase Okay, if it's the way things work, we can get used to it. Since JSX is different from HTML in a lot of ways, it's somewhat justifiable. Wait a moment. We actually use in JSX, but only if we would use Preact instead of can class React. const ExamplePreactComponent = () => { return <div class="foo">Example Preact Component</div> } And it's a legitimate feature, not a coincidence or a bug. So, the question is - why? Why do we all HTML attributes in React, but not in Preact? documented have to camelCase Disclaimer: If you aren't familiar with JSX, but want to read and understand this article anyway, check out , where we take a look at what JSX is and how it works under the hood. my other article The Reason Behind the Rule First thing first, let's clearly define the reason behind this rule in React. The official React docs have a quite vague explanation. Since JSX is closer to JavaScript than to HTML, React DOM uses property naming convention instead of HTML attribute names. camelCase It's hard to say solely from this explanation what the real reason is. So, let's google it and try to find more info! It's a Reserved Keyword There is about this problem on GeeksForGeeks. Let's consider an explanation from it. an article The only reason behind the fact that it uses className over class is that the is a reserved keyword in JavaScript and since we use JSX in React which itself is the extension of JavaScript, we have to use className instead of the class attribute. class First of all, yeah, technically speaking is a reserved keyword in JavaScript for making, so-called, like this one. class class declarations class Polygon { constructor(height, width) { this.area = height * width; } } But we actually use keyword in JavaScript without much trouble. can class const obj = { class: 'value' }; const otherObj = {}; otherObj.class = 'value'; You may think, it didn't work last time I checked! And you'll be right. This works only in modern versions of JavaScript. So that's the point? Not exactly. In older versions of JavaScript, you may easily achieve the same thing by explicitly turning the property into a string literal like so. class const obj = { 'class': 'value' }; const otherObj = {}; otherObj['class'] = 'value'; Okay, maybe the real reason is separate from this whole reserved-keyword issue. Maybe, it's the JSX itself! It's a JSX-specific issue Just think about it. JSX is an of JavaScript, not one-to-one clone or so. That's why even though it's tightly coupled with JS, it may propose some other restrictions. extension Let's battle-test this theory. We'll declare a simple component with a attribute. className const example = <div className="foo">Hello world!</div> Then, we'll put it through Babel transpiler. const example = /*#__PURE__*/React.createElement("div", { className: "foo" }, "Hello world!"); in Babel REPL, in case you want to check yourself. Live example The result is pretty much expected and fully valid. Now let's try another one. Let's use instead of in this try. class className const example = <div class="foo">Hello world!</div> And after transpilation we get this. const example = /*#__PURE__*/React.createElement("div", { class: "foo" }, "Hello world!"); of this try in Babel REPL. Live example First of all, it's fully valid, as well as, the former one. Secondly, Babel transpiles this snippet, like it was nothing new or weird for him. So, it seems like JSX isn't an issue either. Okay, maybe we'll face some issues in the render phase. Because JSX in itself is just syntax and it doesn't create UI on its own. We need to render JSX somewhere to see the end UI. So we'll try to do exactly that to see, if some problems may arise. It's a Render Function Problem Let's create a simple render function from scratch because obviously React won't allow us to use its render mechanism with instead of . Our render function will render the result of to the DOM. But what does the result of look like? class className React.createElement React.createElement returns, so-called, . React.createElement virtual node It looks like this in our case. const example = { $$typeof: Symbol(react.element), key: null, ref: null, props: { class: "foo" }, type: "div", children: ["Hello world!"], _owner: null } But what is a virtual node anyway? Virtual node or vnode, in short, is just a lightweight representation of a given UI structure. In the case of the browser, the virtual node represents the real DOM node. React uses virtual nodes to construct and maintain, so-called, virtual DOM, which itself is a representation of real DOM. Sidenote: If you want to dig into this whole virtual madness, let me know in the comments and I'll make an article, where we'll go through the whole concept of virtual DOM and make our own implementation of it. To implement the render function and check how things work, we only need three basic properties of the vnode. const example = { // defines the type of a given vnode type: "div", // defines all passed React props and HTML attributes of a given vnode props: { class: "foo" }, // contains children of a given vnode children: ["Hello world!"], } Sidenote: If you want to understand what other properties are and why they are here, let me know in the comments section and I'll make detailed articles with a deep explanation of each individual property. Now with new knowledge, we are fully ready to create our own render function for vnode tree. Let's start with the basics and create elements of the passed type. const render = (vnode) => { const el = document.createElement(vnode.type); return el; } Then let's handle the props. const render = (vnode) => { const el = document.createElement(vnode.type); const props = vnode.props || {}; Object.keys(props).forEach(key => { el.setAttribute(key, props[key]); }); return el; } Next, let's recursively add our children and handle edge-case, in which a child is a string. const render = (vnode) => { if (typeof vnode === 'string') return document.createTextNode(vnode); const el = document.createElement(vnode.type); const props = vnode.props || {}; Object.keys(props).forEach(key => { el.setAttribute(key, props[key]); }); (vnode.children || []).forEach(child => { el.appendChild(render(child)); }); return el; } The last missing piece is actual mounting. So let's do it now. const renderedExample = render(example); document.querySelector('#app').appendChild(renderedExample); Now we're good to go. It's time to test how the render function will handle our virtual node with the prop. class It works like a charm! on CodeSandbox. Live example It renders the with correct class . div foo <div class="foo">Hello world!</div> I added this simple bit of CSS to test if our class is in place. And it is, you can verify it yourself! .foo { color: coral; } Now we are completely sure, that the reason behind usage is not connected somehow to render function. We are sure because we implemented the render function, that uses ourselves. className class Now what? Maybe we should agree that it's some kind of convention and leave things as they are? No, we should take an even closer look at the problem. A Different Approach to the Problem You see, there is a JS framework, called . It's an alternative to React with the same API. And there is a very interesting statement on its official page. Preact . Hmm, it's the exact thing, we are looking for. We try to use , which is a native way of adding CSS classes in DOM. And Preact uses this approach, it becomes clear from its . Closer to the DOM class official docs Preact aims to closely match the DOM specification supported by all major browsers. When applying to an element, Preact whether each prop should be set as a property or HTML attribute. This makes it possible to set complex properties on Custom Elements, but it also means you can use attribute names like in JSX: props detects class // This: <div class="foo" /> // ...is the same as: <div className="foo" /> So, let's dig into Preact source code to figure out why it works. Explore Source Code Here is a to the source file on GitHub, in case you want to follow along. link Let's take a look at Preact function, which serves similar purpose as . Here's a snippet from the function body. createElement React.createElement function createElement(type, props, children) { let normalizedProps = {}, key, ref, i; for (i in props) { if (i == 'key') key = props[i]; else if (i == 'ref') ref = props[i]; else normalizedProps[i] = props[i]; } // ... Preact function filters out only two properties, and , and passes others to . createElement key ref normalizedProps and and how these special props are handled internally by Preact, let me know in the comments section. I'll make detailed articles about these two props. Sidenote: If you're asking yourself, why Preact filters out key ref Then Preact passes the resulting to another function, called , and returns the result. normalizeProps createVNode // ... return createVNode(type, normalizedProps, key, ref, null); } Let's dig into function. createVNode Source file on GitHub function createVNode(type, props, key, ref, original) { const vnode = { type, // No props transformation here props, // ... }; // ... // No props transformation here either // ... return vnode; } It becomes obvious from the snippet, that the function doesn't do any transformations with passed . It just returns the in the new object. And object is just a representation of a given DOM element and it'll be rendered to the real DOM in the future, as we now know. createVNode props props vnode vnode So the question is, how does Preact know either it is a complex property or HTML attribute if it passes all properties directly to the , that gets rendered in the end? For example, how does the event system work in this setup? vnode Maybe the answer lies in the render phase? Let's give this guess a shot. There is a function, called , which is responsible for setting a property value on a DOM node, as you may have gathered. This function is the main mechanism of setting properties to DOM nodes in Preact. setProperty Source file on GitHub function setProperty(dom, name, value, oldValue, isSvg) { // ... else if (name[0] === 'o' && name[1] === 'n') { // ... dom.addEventListener(name, handler) } } So Preact actually checks whether the property name corresponds to some event and adds an event listener if it's the case. Such distinction allows Preact to deal with events passed through , , and other props like these, but at the same time allows to use standard HTML properties, like instead of unique-to-JSX . But how does Preact handle user-defined custom props? The answer lies in the question itself. onClick onInput class className You see, we as a developers, may only pass custom properties to our own components. For example, let's define custom . UserDefinedComponent // UserDefinedComponent.js import { h } from 'preact'; const UserDefinedComponent = ({exampleFunc, brandText}) => { exampleFunc(); return ( <div> <p>{brandText}</p> </div> ); } export default UserDefinedComponent; And render it in the component. App // App.js import { h } from 'preact'; import UserDefinedComponent from './UserDefinedComponent'; const App = () => { return ( <UserDefinedComponent exampleFunc={() => { console.log('Hello world!') } brandText="Hello world!" /> ) } As you may see, there is no way how and would be passed to the real HTML elements. And even if you intentionally do this, the browser will just ignore unknown properties, Preact doesn't need to additionally validate them on its side. exampleFunc brandText property naming convention instead of HTML attribute names, anyway? But why does React use camelCase The Last Question There is no clear answer to this question. We may only make a few guesses. Maybe, it's really just a convention, that was proposed when React wasn't event public. Or maybe, React developers want to match more closely, than HTML one. Because in JS the standard way to access class property is . the JavaScript API Element Element.className const element = document.querySelector('.example'); const classList = element.className; element.className = 'new-example'; It doesn't really matter at this point why they've done so. What matters is, that we now understand all nitty-gritty details about it! Wrap up Today we learned Let's sum up what we learned today. The reason why React uses the property is probably not one of these: camelCase is a reserved keyword in JavaScript class properties can't be handled by JSX camelCase properties mess up render function camelCase Preact uses standard HTML properties, because: It aims to closely match the DOM specification It detects whether each prop should be set as a property or HTML attribute Digging into source code is more fun, than frightening 😄 Follow me on , if you want to know about every article I made and also read their sum-ups in threads. Twitter