Very Confusing differences between JSX and HTML
When I work with both JSX and HTML, I get tripped up by subtle differences between the two languages. In this post, I'll share the differences that I find Very Confusing.
πΆπ½ββοΈ Before we get started, I want to clarify some definitions. You can skip this first half if you're already familiar with React! ππ»ββοΈ
What is JSX (intuitively)?
JSX is the syntax that goes in your React components*.
*(You can use JSX with frameworks other than React, but that's beyond the scope of this post.)
import React from "react";
export const MyReactComponent = () => (
<div>
<h1>Hi there! This is My Component.</h1>
<h2>Isn't it cool?</h2>
</div>
);
The syntax after MyReactComponent = () => (
is JSX. It looks like HTML. That's intentional!
When you run your React app, the rendered component will look similar to what this raw HTML page would render:
<!DOCTYPE html>
<html>
<body>
<div>
<h1>Hi there! This is My HTML page.</h1>
<h2>Isn't it cool?</h2>
</div>
</body>
</html>
I learned HTML before JSX, so I use HTML as a mental model of how JSX works. I look at the JSX in my React component and imagine an equivalent HTML page. (I wonder if it's the other way around for developers who learned JSX first π€?)
Since JSX and HTML are so similar, in the few situations where they differ, my mental model model can be really deceptive π±.
Ok but like what actually is JSX?
Fair question. When I say JSX in this post, I'm hand-wavily referring to JSX + React + ReactDOM + the DOM.
In the purest sense, JSX is just a language that compiles into JS. If you compile the code sample above for React with Babel, you'll get this equivalent JS code.
import React from "react";
export const MyReactComponent = () =>
React.createElement(
"div",
null,
React.createElement("h1", null, "Hi there! This is My Component."),
React.createElement("h2", null, "Isn't it cool?")
);
This isn't HTML in any sense. JSX never** gets "converted" to HTML, that's just a helpful mental model. Instead, JSX gets compiled to this tree of JS function calls.
**(I am kinda lying here again.)
At runtime, React magically transforms that tree into actual DOM elements. In other words, you can loosely imagine React as the "bridge" that gets your JSX to show up as actual DOM elements in the browser.
This behavior is similar to how your browser reads an HTML page and constructs the DOM based on that.
In summary, JSX and HTML look similar as source code, and they create similar end results in the DOM.
Differences between JSX and HTML
Ok! With all those definitions out of the way π , I'm going to talk about the differences between JSX and HTML that Very Confuse me.
Boolean props
If you have this element in JSX, what do you think it does?
<button disabled />
Intuitive meaning:
Let's look at some other syntax to see if we can figure out what this means. There are two similar syntaxes, one in JS and one in HTML.
In JS, these two are equivalent:
const disabled = false; // or true
const props = { disabled };
// is equivalent to
const disabled = false; // or true
const props = { disabled: disabled };
In HTML, this syntax means disabled is true
:
<button disabled />
Which one do you think JSX follows?
Actual meaning:
When debugging JSX, I often find it helpful to compile the JSX to JS. (Is that a sign of a leaky abstraction π€?) Let's compile it.
<button disabled />;
// compiles to
React.createElement("button", { disabled: true });
Turns out that JSX matches the HTML behavior. A prop with no specified value gets passed as true
. This wasn't a difference between JSX and HTML after all.
When does this occur?
Usually I introduce boolean props accidentally when refactoring code. I'll start with something like this:
const props = {
name,
onClick: () => setName(name),
placeholderText: "Enter your name",
};
<NameDisplay {...props} />;
and I'll decide to rewrite it without the {...props}
spread:
<NameDisplay
name
onClick={() => setName(name)}
placeholderText="Enter your name"
/>
But! I forget to expand name
into name={name}
.
Using ESLint or TypeScript is helpful in this situation, since it will warn me that I never used the variable name
until I write name={name}
.
Numeric props
Is this valid JSX?
<input maxLength="10" />
Sort of. It will work π€·πΌββοΈ. However, if you're using TypeScript, you'll get a compilation error saying that you can't pass a string for maxLength
.
This is different from HTML, where maxlength="10"
is acceptable syntax.
In HTML, all attributes are strings (well, close enough). So if you want to set an attribute to a number, you set maxlength="10"
and internally the DOM will transform that "10"
string into a number.
JSX, however, supports multiple ways of passing props. There's maxLength="10"
and maxLength={10}
.
maxLength={10}
passes the JS expression between{}
, which is10
in this case.maxLength="10"
passes a string.- You can think of it as shorthand for
maxLength={"10"}
.
- You can think of it as shorthand for
<input maxLength="10" />;
// compiles to
React.createElement("input", {
maxLength: "10",
});
<input maxLength={10} />;
// compiles to
React.createElement("input", {
maxLength: 10,
});
<input maxLength={"10"} />;
// compiles to
React.createElement("input", {
maxLength: "10",
});
You can imagine that React.createElement("input", ...)
has a type definition that only accepts certain props:
interface InputElementProps {
maxLength?: number;
}
Which means that { maxLength: "10" }
would fail the type check, like we saw in the original error message for <input maxLength="10" />
.
Self-closing tags
A self-closing tag is a tag that doesn't need a corresponding closing tag.
The syntax ends with />
, like <div />
.
The behavior of the />
syntax is pretty different between JSX and HTML. I've found this difference most confusing when I've been working with JSX for a while, then go back to editing a legacy HTML template, since I'll expect the HTML to behave the same way JSX does. My HTML page won't throw a compilation error to tell me I did it wrong, either π.
JSX
In JSX, the <div />
syntax is equivalent to <div></div>
.
It's the same behavior, regardless of the element's type. (div
, br
, MyComponent
, circle
, ...) That's it. Try it out in your compiler and see that they compile to the same thing.
HTML
...It's complicated.
First off, if you look back at XHTML (we don't talk about XHTML) or HTML4, the behavior is different. I'll ignore this fact and only cover modern HTML (i.e., HTML5, documents that start with <!DOCTYPE html>
).
In HTML, the />
syntax is usually meaningless, but it depends on specifically what element you use it on.
- Normal elements. This set includes elements that can have children, such as
div
,span
andh1
..- This syntax for normal elements is invalid HTML. However, browsers will treat the tags like opening tags. So
<div />
will be treated as<div>
, likely causing you to forget the (still necessary) closing</div>
tag.
- This syntax for normal elements is invalid HTML. However, browsers will treat the tags like opening tags. So
- Void elements. This set includes elements that cannot have children, such as
img
,input
andhr
.- This syntax for void elements is valid HTML, but has no effect. That means
<hr />
and<hr>
are equivalent, and neither require a closing tag.
- This syntax for void elements is valid HTML, but has no effect. That means
- Foreign elements. This set includes elements in the SVG namespace, such as
circle
.- In this situation, the syntax does denote a self-closing tag, following the behavior we've learned from JSX. So,
<circle />
is the same as<circle></circle>
.
- In this situation, the syntax does denote a self-closing tag, following the behavior we've learned from JSX. So,
I wouldn't recommend memorizing that, or anything. Just be aware that the />
syntax in HTML is haunted and in JSX it is simple π. And also, if you write a blog post and start referencing the HTML spec, you'll have a bad time writing out all the edge cases.
Conclusion
There are plenty more differences between JSX and HTML, and I recommend reading the official React documentation for a more broad overview: JSX in Depth. If you're interested in differences between React's DOM and the DOM, check out DOM Elements.
Anyway. It's a scary world out there. Be careful!