Skip to main content

JSX Attributes: `className`, `htmlFor`, and Other Differences #19

📖 Introduction

Following our look at embedding JavaScript in JSX, we'll now focus on a subtle but crucial aspect of JSX: its attributes. While JSX looks almost identical to HTML, there are a few key differences in how attributes are named. These differences are not arbitrary; they exist because JSX is fundamentally JavaScript.

This article will cover the most common attribute changes, such as className and htmlFor, explain why these changes are necessary, and introduce the general convention of using camelCase for JSX props.


📚 Prerequisites

Before we begin, please ensure you have a solid grasp of the following concepts:

  • HTML Attributes: You should be familiar with common HTML attributes like class, for, and style.
  • JavaScript Reserved Words: A basic understanding that certain words (like class, for, if) have special meaning in JavaScript.
  • JSX Syntax: Comfort with writing basic JSX elements and passing props.

🎯 Article Outline: What You'll Master

In this article, you will learn:

  • The className Attribute: Why you must use className instead of class in JSX.
  • The htmlFor Attribute: The reason behind using htmlFor in place of the for attribute on labels.
  • The camelCase Convention: Understanding the general rule for naming most JSX attributes.
  • Special Exceptions: Recognizing attributes like data-* and aria-* that do not follow the camelCase rule.
  • Practical Examples: Applying these attributes correctly in a React component.

🧠 Section 1: Why Are Some Attributes Different in JSX?

The core reason for the differences between HTML attributes and JSX attributes is that JSX is JavaScript. When your JSX code is transpiled, the attributes you write become keys in a JavaScript props object.

// This JSX:
<div className="profile" />

// Becomes this props object:
{
className: "profile"
}

This means that attribute names must follow the rules for JavaScript identifiers. They cannot be reserved words, and they cannot contain hyphens (which would be interpreted as subtraction).


💻 Section 2: The Most Common Differences

Let's look at the two most common attribute changes you'll encounter.

2.1 - class becomes className

In HTML, you use the class attribute to assign CSS classes to an element.

The Problem: class is a reserved keyword in JavaScript, used to define classes with the class MyClass { ... } syntax. If you used class as a prop name, it would cause a syntax error in JavaScript.

The Solution: React uses className instead. When React renders the component to the DOM, it will correctly convert the className prop back into a class attribute in the final HTML.

// code-block-1.jsx
import React from 'react';
import './styles.css'; // Assuming styles.css contains .active { color: green; }

function StatusMessage() {
const isActive = true;

// We use className to apply a CSS class.
return (
<div className={isActive ? 'active' : 'inactive'}>
Current Status
</div>
);
}

export default StatusMessage;

Rendered HTML:

<div class="active">Current Status</div>

2.2 - for becomes htmlFor

In HTML, the <label> element's for attribute is used to associate it with a form input, improving accessibility.

The Problem: Just like class, for is a reserved keyword in JavaScript, used for for loops.

The Solution: React uses htmlFor to achieve the same functionality without conflicting with the JavaScript keyword.

// code-block-2.jsx
import React from 'react';

function LoginForm() {
return (
<form>
{/* htmlFor associates this label with the input below */}
<label htmlFor="usernameInput">Username:</label>
<input type="text" id="usernameInput" />
</form>
);
}

export default LoginForm;

Rendered HTML:

<form>
<label for="usernameInput">Username:</label>
<input type="text" id="usernameInput">
</form>

Clicking on the "Username:" label in the browser will correctly focus the text input, just as it would in plain HTML.


🛠️ Section 3: The camelCase Convention

Beyond the specific reserved words, there's a general convention for multi-word HTML attributes. Because hyphens (-) are not valid in JavaScript identifiers, they are converted to camelCase.

HTML Attribute -> JSX Attribute

  • accept-charset -> acceptCharset
  • http-equiv -> httpEquiv
  • stroke-width (in SVG) -> strokeWidth
  • tabindex -> tabIndex (Note: even though there's no hyphen, it follows the DOM API property name)
// code-block-3.jsx
import React from 'react';

function CustomInput() {
return (
<input
type="text"
// The HTML tabindex attribute is written as tabIndex in JSX
tabIndex={0}
placeholder="I have a tab index"
/>
);
}

export default CustomInput;

🚀 Section 4: Exceptions to the Rule: data-* and aria-*

There are important exceptions to the camelCase rule. For accessibility and custom data purposes, aria-* and data-* attributes are written exactly as they are in HTML, with hyphens.

React makes this exception to align with the web standards for these specific attribute types.

// code-block-4.jsx
import React from 'react';

function AccessibleComponent() {
const hasError = true;

return (
<div
// Custom data attributes remain as-is
data-testid="user-greeting"
// ARIA attributes for accessibility also remain as-is
aria-live="polite"
aria-atomic="true"
role="status"
>
{hasError ? 'An error occurred.' : 'Data loaded successfully.'}
</div>
);
}

export default AccessibleComponent;

✨ Section 5: Best Practices

Best Practices:

  • Trust Your Linter: A properly configured linter (like ESLint with the React plugin) will warn you if you accidentally use class instead of className or for instead of htmlFor. This is your best defense against these common mistakes.
  • Refer to the Docs: When in doubt about a specific attribute, the official React documentation on DOM Elements is the ultimate source of truth.
  • Consistency is Key: Always use camelCase for attributes unless they are data-* or aria-* attributes.

💡 Conclusion & Key Takeaways

Understanding the nuances of JSX attributes is a key step in moving from writing HTML to thinking in React. These differences are purposeful, designed to make JSX work seamlessly within the JavaScript ecosystem.

Let's summarize the key takeaways:

  • class -> className: The class attribute is renamed to className to avoid conflict with the JavaScript class keyword.
  • for -> htmlFor: The for attribute is renamed to htmlFor to avoid conflict with the JavaScript for loop.
  • camelCase is the Standard: Most multi-word HTML attributes are written in camelCase in JSX (e.g., tabindex becomes tabIndex).
  • Exceptions for Data and ARIA: data-* and aria-* attributes are the main exceptions and should be written with hyphens, just like in HTML.

Challenge Yourself: Create a simple registration form component with fields for an email and a password. Ensure that each <input> has a corresponding <label> and that they are correctly associated using the htmlFor attribute. Apply some simple styling using className.


➡️ Next Steps

You've now mastered the key differences between HTML and JSX attributes. In the next article, "The Rules of JSX (Part 1): Single root element and the power of Fragments (<>...</>)", we will cover one of the most fundamental rules of writing JSX and introduce a powerful feature for keeping your DOM clean.

Thank you for your dedication. Stay curious, and happy coding!


glossary

  • camelCase: A naming convention where the first word is lowercase and subsequent words are capitalized, with no spaces or hyphens (e.g., htmlFor, backgroundColor).
  • Reserved Word: A word that has a special meaning in a programming language and cannot be used as an identifier (like a variable or property name). class and for are reserved words in JavaScript.
  • DOM API: The programming interface for web documents. It represents the page so that programs can change the document structure, style, and content. JSX attributes often align with the property names in the DOM API (e.g., element.className).

Further Reading