Volume I index
JSX Is Real React Code

Attributes, Class Names, and Styles

Ishtmeet Singh @ishtms/July 14, 2026/8 min read
#react#jsx#attributes#css#accessibility

JSX attributes become props on a React element. When the element type is a lowercase host name, React DOM interprets those props and updates browser properties, attributes, styles, and event listeners.

jsx
<a className="lesson-link" href="/learn/jsx">
  Read the JSX chapter
</a>

className and href are React element props. React DOM applies a class attribute and link destination to the browser anchor.

Custom components use props according to their own contracts. Host elements use the React DOM prop names documented for the web.

Host Props and Component Props Have Different Owners

This host element uses React DOM's supported button props.

jsx
<button type="button" disabled={false}>
  Continue
</button>

This custom component can define application-specific prop names.

jsx
<ContinueButton available lessonId="jsx" />

available and lessonId mean whatever ContinueButton implements. They do not automatically appear on the nested browser button.

jsx
function ContinueButton({ available, lessonId }) {
  return (
    <button type="button" disabled={!available}>
      Continue {lessonId}
    </button>
  );
}

The component reads its public props and decides which supported host props and children reach the DOM.

Use className for CSS Classes

HTML source uses class. JSX uses className.

jsx
<article className="lesson-card">Props as Inputs</article>

The browser DOM receives a class attribute. React follows the DOM property name className in JSX.

This incorrect form can produce a development warning.

jsx
<article class="lesson-card">Props as Inputs</article>

Use one string for several classes.

jsx
<article className="lesson-card lesson-card--featured">
  Props as Inputs
</article>

Calculate the string before JSX when classes depend on props.

jsx
const className = featured
  ? "lesson-card lesson-card--featured"
  : "lesson-card";
jsx
return <article className={className}>{title}</article>;

The component supplies a complete string on every render. React DOM updates the class when the string changes.

Build Conditional Class Strings

Template strings work for a known variant.

jsx
const className = `status status--${status}`;

Use this only when status comes from a supported set such as reading or complete. An unchecked value can create a class that has no stylesheet rule.

Several independent classes can be assembled from an array.

jsx
const classes = ["lesson-card"];

if (featured) classes.push("lesson-card--featured");
if (complete) classes.push("lesson-card--complete");

const className = classes.join(" ");

The array is local to this render, so pushing into it does not mutate props or shared state.

Class helper libraries can become useful when a project repeats complex conditional combinations. The first app does not need another dependency for two boolean classes.

JSX Uses DOM Property Names

Several HTML names differ in JSX.

jsx
<label htmlFor="reader-email">Email</label>
<input id="reader-email" readOnly tabIndex={0} />

HTML for becomes htmlFor. readonly becomes readOnly. tabindex becomes tabIndex.

React DOM documents the supported prop casing. Development warnings often suggest the correct form after a casing mistake.

Do not convert every hyphenated HTML name to camel case. Two important families retain hyphens.

jsx
<button
  aria-expanded={false}
  aria-controls="chapter-details"
  data-chapter-id="jsx-real-react-code"
>
  Show details
</button>

ARIA and custom data attributes use lowercase hyphenated names.

Boolean Host Props Need Boolean Values

Use JavaScript booleans for properties such as disabled, required, readOnly, multiple, and hidden.

jsx
<button disabled={!available}>Continue</button>

This string is not the boolean false.

jsx
<button disabled="false">Continue</button>

In HTML, the presence of a boolean attribute represents the enabled property state for that attribute. A string spelling of false does not mean the attribute is absent.

Use braces.

jsx
<button disabled={false}>Continue</button>

Boolean shorthand supplies true.

jsx
<button disabled>Continue</button>

ARIA attributes follow ARIA value definitions rather than HTML boolean-attribute rules. For example, aria-expanded={false} is serialized to communicate the false state. Removing the attribute would mean that the expanded state is not being conveyed.

Use ARIA only when the element and interaction need it, and follow the supported value for that role or property.

Accessible Names Begin with HTML

React does not add accessible names automatically.

A text button receives its name from its content.

jsx
<button type="button">Save reading goal</button>

An icon-only button needs a name.

jsx
<button type="button" aria-label="Close chapter menu">
  <CloseIcon aria-hidden="true" />
</button>

The icon is hidden from the accessibility tree because the button already provides the action name.

Form controls should use labels.

jsx
<label htmlFor="reader-name">Reader name</label>
<input id="reader-name" name="name" />

The htmlFor value matches the input id. Activating the label focuses the control.

A placeholder does not replace the label.

jsx
<input placeholder="Reader name" />

Placeholder text can disappear during entry and does not provide the same persistent labeling behavior.

Give Images the Right Alternative

An informative image needs an alternative that communicates its purpose.

jsx
<img
  src="/covers/volume-1.png"
  alt="ReactBook Volume I cover"
/>

A decorative image uses an empty alternative.

jsx
<img src="/stars.svg" alt="" />

Do not omit alt for a decorative image. An empty string marks it as decorative and removes it from normal image announcement. A missing attribute can cause assistive technology to announce the filename.

When nearby text already supplies the exact information, empty alternative text can prevent duplicate announcement.

The style Prop Receives an Object

HTML can use a style string. React JSX uses an object.

jsx
<div style={{ width: "50%", backgroundColor: "#61dafb" }} />

The outer braces enter JavaScript. The inner braces create the object literal.

CSS property names use camel case.

text
background-color -> backgroundColor
margin-top       -> marginTop
z-index          -> zIndex

React DOM converts the object entries into inline styles on the host element.

This string form is invalid for normal React DOM style usage.

jsx
<div style="width: 50%" />

Pass the object reference instead.

jsx
const progressStyle = { width: "50%" };
return <div style={progressStyle} />;

Numeric Style Values and Units

React adds px to many numeric style properties.

jsx
<div style={{ width: 320, marginTop: 16 }} />

The resulting inline declarations use 320px and 16px.

Selected CSS properties are unitless.

jsx
<p style={{ opacity: 0.8, lineHeight: 1.6, zIndex: 2 }}>
  Current lesson
</p>

Supply an explicit string for another unit.

jsx
<main style={{ minHeight: "100vh", maxWidth: "70rem" }} />

Do not append units to every number through a generic helper. The correct unit belongs to the specific CSS property and design value.

Classes and Inline Styles Solve Different Cases

Use a class for stable presentation and selector-based states.

css
.lesson-link {
  background: #61dafb;
  color: #06141b;
}

.lesson-link:focus-visible {
  outline: 3px solid white;
}
jsx
<a className="lesson-link" href="/learn">Start reading</a>

Use inline style for a value calculated by component data.

jsx
const width = `${percentage}%`;
return <div className="progress-fill" style={{ width }} />;

Inline styles cannot express every selector, media query, keyframe, or stylesheet cascade rule. Classes also provide a stable vocabulary for design review.

Reuse a Style Object Only When It Is Static

An object created during each render is supported.

jsx
function Progress({ percentage }) {
  return <div style={{ width: `${percentage}%` }} />;
}

Move a constant object outside the component when every occurrence uses the same values.

jsx
const decorativeStyle = {
  pointerEvents: "none",
  userSelect: "none",
};

Do not mutate that shared object later. Treat style objects as read-only descriptions just as React element props are treated.

Manual memoization of style objects is not needed here. Add it only when profiling or an explicit reference contract demonstrates a benefit.

Spread DOM Props with a Public Contract

A wrapper can forward selected DOM props.

jsx
function TextField({ className = "", ...inputProps }) {
  return (
    <input
      {...inputProps}
      className={`text-field ${className}`.trim()}
    />
  );
}

The component consumes className, gathers the remaining props, spreads them onto the input, and then supplies the final combined class.

Order controls which repeated prop wins.

jsx
<input className="fixed" {...inputProps} />

Here a caller's className inside inputProps can replace fixed.

jsx
<input {...inputProps} className="fixed" />

Here fixed replaces the caller's class.

Combine supported values in a defined order instead of relying on accidental order.

Do not spread an arbitrary data record onto a host element.

jsx
<article {...lesson} />

The object can contain internal fields, invalid DOM props, or callback values that should not reach the browser. Extract the host props the component supports.

Inspect What Reached the Browser

Select the element in the browser Elements panel. Confirm the final class, ARIA attributes, data attributes, and inline style declarations.

Then inspect computed styles. A correct React prop can still lose in the CSS cascade or inherit an unwanted value.

React DevTools shows the component props supplied to the custom component. The Elements panel shows the host result. Use both when a wrapper transforms public props before reaching the DOM.

Check the Host Prop Path

Each step interprets a different part of the host prop path.

text
JSX creates element props
custom components interpret their own prop contract
React DOM interprets props on lowercase host elements
the browser applies DOM, CSS, and accessibility behavior

Source Notes