What is JSX?
In the previous chapters, we've already seen what JSX is and have even put it into action. But let's explore more details of this amazing extension to JavaScript's syntax, specifically made for React.
To start with, let's review what JSX is.
It was developed by Meta (formerly Facebook) to be used alongside React, and released shortly after React in 2013.
As you already know at this stage, JSX is meant to simplify the construction of React elements, otherwise done by calling createElement()
. If offers an HTML-like (or XML-like) syntax to denote React elements in JavaScript source code.
Keep in mind that JSX is neither a part of the ECMAScript standard, nor of the HTML or XML standards. It's purely a standalone specification which implementors can go through and implement in their JavaScript parsers, like Babel.
The JSX specification, alongside the motivation behind JSX, can be read at https://facebook.github.io/jsx/.
As we've seen in the previous chapters, JSX is absolutely NOT a requirement in React apps, per se. But it's so compact and rewarding over the createElement()
-call approach that we feel that when we talk React, we are bound to talk JSX. We just feel at home while writing JSX.
Stating it once more, JSX is not a part of React — it's a separate syntactic tool to help React developers easily create React elements using a syntax similar to that of one of the most rudimentary, yet paramount, web technologies, i.e. HTML.
JSX is clearly one of the many reasons of the popularity of React, even though it's a disparate feature.
Now if we delve into the technical side, we see that JSX is precisely a type of expression in JavaScript. JSX is NOT a statement; it's purely an expression. Hence, wherever JavaScript expects an expression, we can indeed provide JSX there.
For example, we can assign JSX to a variable; write JSX as an array item; pass on JSX to a function as an argument; write JSX as an attribute's vaue of a JSX element (which we'll see below); and so on. We'll see suchlike examples throughout this course.
JSX elements
As we've become quite familiar with them, JSX elements are simply syntactic sugar over creating React elements manually via createElement()
.
The syntax of JSX elements resembles that of HTML elements.
That is, we have opening and closing tags denoted using angle brackets (<
and >
). This makes perfect sense because all UI libraries/frameworks, at the end of the day, create HTML elements. So why not use an HTML-like syntax if our code is to be used to create HTML elements anyway.
React comes with a handful of predefined elements which are its abstractions over HTML DOM elements. And in JSX, they are represented just like they'd be in normal HTML source code.
For example, if we want to create an <h1>
element in React, we'd write the following JSX:
<h1>This is JSX</h1>
Similarly, if we want to create a <div>
element, we'd write the following JSX:
<div>A simple div</div>
And so on and so forth.
All HTML elements are available in React with the exact same tag names.
And not just this, but JSX elements can also be used to instantiate components in React.
For example, in the code below, we instantiate a <Greeting>
element out of the Greeting
component:
function Greeting() {
return (
<h1>JSX is superb!</h1>
);
}
const element = <Greeting></Greeting>;
Simple, isn't this?
One extremely important thing to note while instantiating components via JSX elements is that the name of the component must be capitalized (i.e. begin with a capital letter). This is NOT a convention; it's a must.
The following snippet expands upon this idea.
Capitalization of component names required for JSX elements
If the name of a component doesn't begin with a capital letter, the JSX element used to instantiate it would get converted into a createElement()
call where the first argument is NOT a reference to the component, but instead a string containing the name of the component.
And this would simply lead to unexpected results.
Let's see an example.
In the following code, we have a component called greeting
(with the first letter in lower case) and then, as always, instantiate it via <greeting>
:
function greeting() {
return 'JSX is simple';
}
const element = <greeting></greeting>;
However, when we run the code, we notice nothing on the document. What possibly could've gone wrong?
The console might help us out. Well, yes, we do see a warning messaged logged in the console:
Before making sense of this warning, let's see what the JSX converts to:
const element = React.createElement('greeting', null);
Carefully notice the first argument here. It's a string, NOT a function, contrary to our expectation.
Recall from the previous React Elements chapter that when the first argument of createElement()
is a string, it represents a DOM element. Now since there is no such DOM element as <greeting>
in the browser, we get the warning above.
It's also important to note that this element (representing a non-existent DOM element) does get rendered out on to the document but without any content whatsoever inside it. In the link above, open up Developer Tools and inspect the DOM tree of the page; you'll notice a <greeting>
element in there.
Anyways, the solution to this complex mess is pretty simple: capitalize the component's name.
Let's do this now:
function Greeting() {
return 'JSX is simple';
}
const element = <Greeting></Greeting>;
And it works now!
With this simple idea of capitalization, of a component name, a JSX parser is easily able to distinguish between a React element representing a DOM element and a React element representing a component instance.
React doesn't have to maintain a list of DOM element names to check against the tag name of a given JSX element in order to determine whether it represents a DOM element or a component instance — this distinction is right there in the JSX code itself.
Moving on, if a JSX element doesn't have any children, we could omit its closing tag. This follows exactly from how we denote void elements in HTML.
So, the <Greeting>
element shown above could be more compactly denoted as follows:
function Greeting() {
return (
<h1>JSX is superb!</h1>
);
}
const element = <Greeting/>;
Keep in mind that when we do omit the closing tag, then it's a requirement to put a forward slash (/
) at the end inside the starting tag.
Likewise, the following is invalid JSX:
function Greeting() {
return (
<h1>JSX is superb!</h1>
);
}
// The closing / is missing in the tag.
const element = <Greeting>;
This applies to React elements representing DOM elements as well.
For example, in the following code, we denote an <img>
element as a single tag but without a /
inside it (as we're used to doing in HTML sometimes) and thus get an error logged:
// The closing / is missing in the <img> tag.
const element = (
<img src="image.png" alt="Some image">
);
The correct way to express this is as follows:
const element = (
<img src="image.png" alt="Some image" />
);
The <img>
tag here includes the /
character at its end and, therefore, denotes valid JSX.
Always make sure to include /
at the end of tags in your JSX that don't have corresponding closing tags, because it could otherwise lead to errors.
JSX attributes
Since JSX elements mirror the syntax of HTML/XML elements, it shouldn't be any surprising to know that we could set attributes on JSX elements too.
JSX attributes work pretty much the same way as attributes on HTML/XML elements. But it's paramount to always remember that JSX elements are simplifications over createElement()
and so, by that means, attributes merely represent given props of React elements.
There's almost nothing special to learn for JSX attributes apart from the fact that they're just props of the underlying element. So the way those attributes (on the JSX element) work is exactly the way the corresponding props work.
As simple as that.
The most important thing to remember is that, except for data-
and aria-
attributes, all JSX attributes are represented in the camel-case convention.
data-src
, aria-label
, accept-charset
, readonly
, and so on).For example,
- In HTML we have the
contenteditable
attribute, yet in JSX we have thecontentEditable
attribute. - In HTML we have
readonly
, while in JSX we havereadOnly
. - In HTML, we have
accept-charset
, while in JSX we haveacceptCharset
.
And so on and so forth.
If we consider the fact that JSX is closer to JavaScript than HTML — in fact, it is JavaScript at the end of the day — this won't be any hard to wrap the mind around.
Still let's have a quick formal dicussion on this.
Why are DOM-related props camel-cased in React?
This is a very good question, and one which might've popped up in your mind as well. The answer to it is fairly simple.
Restating the question: Why exactly are DOM-related props in React, or simply DOM-related attributes of JSX elements (representing DOM elements) camel-cased?
For instance, why do we have contentEditable
(in React) instead of contenteditable
(in HTML)? Why do we have acceptCharset
(in React) instead of accept-charset
(in HTML)?
Well, there are mainly two reasons for this.
If you carefully inspect it, many of the HTML attributes are represented as camel-cased properties in the DOM. We don't need to go far away to confirm this. The contenteditable
attribute in HTML is represented as contentEditable
in the DOM.
And because React is a library that works on top of the DOM, it makes sense to stick to a naming convention that's used by the DOM.
So this is one reason why React names DOM-related props in the camel-case convention. That is, using camel-case goes with the very nomenclature used in the DOM itself.
The second reason is more related to code consistency.
Imagine if these props weren't camel-cased. And at the same time, also suppose that we had certain props not related to the DOM at all, i.e. they were just meant for driving the logic of the React app, for e.g. firstName
, items
, etc.
With this scenario set up, we're very likely to be naming the DOM-unrelated props in camel case, as that's what we're used to doing while coding in JavaScript. But the DOM-related props would be in lower case.
You can almost see where we're heading.
If we were to pass both these kinds of props to a component, this difference in the naming of the props (that is, some lowercased while some camel-cased) would've ultimately led to inconsistency.
By sticking to camel case for almost all DOM-related props, consistency in the names of props, both related and unrelated to the DOM, can be achieved.
data-
and aria-
attributes in React
There's an exception to this camel-cased prop naming convention in React.
That is, if we wish to set any data-
or aria-
attribute on any React element, we don't have to worry about camel-casing it — we write it just as we'd do in plain HTML.
For example, suppose we want to set the data-src
attribute on an <img>
React element.
This would be accomplished as follows:
const element = (
<img data-src="image.png" />
);
The attribute is called data-src
, NOT dataSrc
.
Let's consider some commonly-used JSX attributes.
First, we have the className
attribute — perhaps the most important attribute, driving the styling of web apps:
const element = (
<h2 className="text-blue">Hello World!</h2>
);
.text-blue {
color: blue
}
Hello World!
Next, we have style
for inline-styling elements. The style
prop is another one of the many miniature features React provides to us in order to ease UI development.
Setting inline styles on elements from within JavaScript, though not recommended as the go-to approach for styling, is not a very rare thing — it's frequently observed in applications.
In the HTML DOM, we have the style
property which holds an object with loads and loads of style properties on it that we could assign strings to in order to apply the given styles to a given DOM element.
If we want to, we can even take a whole string of CSS text and dump it into the cssText
property of the style
object to bulk-apply many styles at once.
style
property in JavaScript CSSOM — The style
Property.However, we can't assign an object literal, holding the styles, to the style
property as we can do with the style
prop in React. This just does NOT work in the DOM, which explains that React improvises a little bit with style
as well, and rightly so.
With the ability of providing an object literal to style
, holding all the required styles, we always remains in the context of typing actual code while laying out our styles, and never ever have to switch to the context of working in a string (which is tedious).
style
attribute, we get autocompletion. And that's simply amazing!Let's take an example of using style
:
const element = (
<h2 style={{ marginLeft: '50px', color: 'orange' }}>Hello World!</h2>
);
Hello World!
Besides this, if we assign a number to a given style property, let's say width
, or maxWidth
, or maybe even left
, React automatically converts it to the string '<number>px'
, where <number> represents the number entered.
So based on this, we could redefine the marginLeft
property above as shown below, without changing the output of the code:
const element = (
<h2 style={{ marginLeft: 50, color: 'orange' }}>Hello World!</h2>
);
Hello World!
marginLeft: 50
is effectively the same as saying marginLeft: '50px'
.
Moving on, another attribute we might run into while developing our applications is id
:
const element = (
<div id="d1">Hello World!</div>
);
#d1 {
background-color: orange;
color: white;
width: 100px;
}
Hello World!
Quite basic.
Apart from this, when a particular attribute is merely set on a JSX element, without a corresponding value, its value defaults to the Boolean true
.
Once again, this idea has been borrowed from HTML where omitting the value of a given attribute (specifically, a Boolean attribute) defaults it to "true"
.
So for example, the following JSX, setting the disabled
attribute,
const element = (
<button disabled>A disabled button</button>
);
is effectively the same as:
const element = (
<button disabled={true}>A disabled button</button>
);
And this produces the following output:
Embedding code inside JSX
JSX itself is just a static representation of a bunch of createElement()
calls. But because createElement()
is JavaScript, it seems sensible to be able to switch to 'JavaScript mode' while writing JSX and, thus, make the JSX dynamic in that way.
This mode switching is done by using a pair of curly braces ({}
) where JSX code is expected.
A pair of curly braces ({}
) instructs the JSX parser that JavaScript code — or more precisely speaking, a JavaScript expression — follows.
Let's consider a few examples.
In the following JSX code, we create an <h1>
element whose text content refers to a variable language
:
let language = 'JSX';
const element = (
<h2>Using {language}</h2>
);
Using JSX
If we want to, we can also have two different embedded expressions right next to each other, as shown below:
let language = 'JavaScript';
const element = (
<h2>Using {language}{'—a programming language'}</h2>
);
Using JavaScript—a programming language.
As the two expressions are next to each other, without any space in between, the rendered text also has no space in between 'JavaScript' and '—a programming language'.
Besides being used inside elements, {}
can also be used while setting values of attributes. We've already seen examples of these from previous chapters.
Consider the following code where the className
attribute's value is retrieved from a variable blueTextClassName
:
let blueTextClassName = 'text-blue';
const element = (
<h2 className={blueTextClassName}>JSX is splendid</h2>
);
Supposing that the CSS below has been set,
.text-blue {
color: blue
}
the output produced by this code would be as follows:
JSX is splendid.
Just basic stuff.
Now let's see more complex examples.
Suppose we have a variable isTextSmall
that tells us whether to apply a small font size to a <p>
element or not. Based on this variable, we ought to set a class .text--sm
on the element, in addition to the .text
class.
This can very easily be accomplished using JSX and the conditional operator in JavaScript, as demonstrated below:
let isTextSmall = true;
const element = (
<p className={'text' + isTextSmall ? ' text--sm' : ''}>JSX is splendid</p>
);
With the following CSS in place,
.text {
font-family: sans-serif;
font-size: 18px
}
.text--sm {
font-size: 14px;
}
the output produced here is would be follows:
JSX is splendid
The conditional operator of JavaScript is really handy when we want to lay out conditional code inside JSX. We'll see it in more detail later on in this chapter.
Advancing forward, there is a special expression that we could embed inside the starting tag of a JSX element to define a whole set of attributes to use at once; there's no need to one-by-one pass the attributes.
It's the called the spread attribute, and is denoted as {...props}
, where props
is an object containing all the props, i.e. attributes, to set on the JSX element.
Suppose we have a divProps
object with us, as shown below, and want to use all of its properties as the attributes (or simply props) of a <div>
element:
let divProps = {
className: 'text-blue',
style: {
marginTop: 50
},
title: 'This is the title of the div.'
};
Using ...
we can conveniently do so as follows:
let divProps = {
className: 'text-blue',
style: {
marginTop: 50
},
title: 'This is the title of the div.'
};
const element = (
<div {...divProps}>This is a div.</div>
);
The JSX here gets converted to the following code:
const element = React.createElement('div', { ...divProps }, 'This is a div');
Notice how {...divProps}
in the JSX code becomes ...divProps
(using JavaScript's spread operator) in the props
object provided to createElement()
.
The spread attribute carries a lot of potential in it. We can use it to pass an arbitrary set of attributes to a JSX element, which can then further pass those to another element down the chain, and so on.
However, this is kind of an advanced pattern in React which we won't be seeing until we cover other fundamental ideas in this course.
Anyways, moving on, one thing that might confuse you while you're embedding JavaScript inside JSX is that when to and when not to use {}
to denote a JavaScript expression amid JSX code.
Do you find that intimidating?
Well, the idea is rudimentary:
{}
to denote a JavaScript expression wherever JSX is expected.If however, JavaScript itself is expected and we mistakenly provide curly braces, you can surely guess what'll happen — they'll denote an object literal and because the content inside the braces is syntactically invalid for an object literal, they'll likely lead to an error.
Values not rendered
React accepts many different kinds of values as React nodes into the createElement()
function. However, only some are actually rendered out on to the document.
The values that don't get rendered are countless. It's easier to state the values that do get rendered in React, from which we can easily infer the ones that don't.
The values that get rendered in React are strings, numbers, arrays, and React elements; the rest are all void from rendering.
Consider the following example where we try to render the value true
into the <h1>
element, yet we see nothing on the document:
const element = (
<h1>{true}</h1>
);
If we inspect the Developer Tools, we see that there indeed is our <h1>
rendered, but without any content inside it. That's because the value true
doesn't get rendered.
We could try doing the exact same thing with false
, null
, undefined
, and just about any value apart from the four discussed above, and the result will remain the exact same.
But, let's say, if we wish to render either of these non-rendered values (such as undefined
, or true
), we can convert them to a string representation.
And example follows:
const element = (
<h1>{String(true)}</h1>
);
Simple, wasn't this?
JSX comments
Since comments are denoted as follows in HTML:
<div>
<!-- This is an HTML comment -->
A simple div
</div>
and the fact that JSX resembles HTML, we might be tempted to think that JSX comments are denoted the same way.
Unfortunately, that's NOT the case.
A quick try can confirm this:
<div>
<!-- This is a JSX comment -->
A simple div
</div>
There's no native syntax for comments in JSX itself. But this doesn't mean that we can't have comments in our JSX code.
Recall the pair of curly braces ({}
) used to denote an arbitrary JavaScript expression in JSX. Since {}
holds JavaScript, we can use a multi-line JavaScript comment inside it, and in this way, emulate a JSX comment.
Amazing, isn't it?
Here's an example of a comment in JSX:
<div>
{/* This is a JavaScript comment */}
A simple div
</div>
Now because the curly braces hold a JavaScript expression, we might as well be tempted to think that we could use single-line comments in there. And yes, we could, but not without taking some care.
If we denote a single-line comment in the same line as we have the curly braces, the comment would consume the entire line starting with //
. This means that the ending }
brace would become a part of the comment and, thus, leave the starting {
brace unmatched, ultimately leading to a syntax error.
Here's a demonstration:
<div>
{// This is a single-line JavaScript comment}
A simple div
</div>
The correct way to use a single-line comment is to, at the least, make sure that the closing }
brace comes at a new line.
Likewise, the following is correct:
<div>
{// This is a single-line JavaScript comment
}
A simple div
</div>
But almost no one would write a comment like this.
It's better to structure out the code as shown below:
<div>
{
// This is a single-line JavaScript comment
}
A simple div
</div>
In fact, no one would, or should, write a standalone JSX comment like this as well. It overcomplicates the embedded JavaScript expression.
The multi-line comment syntax is much better to use, as we don't have to worry about new lines and could easily remain consistent with it to denote any kind of a comment in JSX code — single-line and/or multi-line.
From this point onwards, we'll denote comments in JSX via {/* */}
.
JSX files (.jsx extension)
Using JSX is so common in the React community that there is a separate file extension to indicate the a file uses JSX. It's called .jsx, and a file with this extension is simply called a JSX file.
There's absolutely no difference in how we write code in a normal JavaScript file vs. in a JSX file — like literally no difference, at all. JSX files are only meant to help us quickly see which file contains JSX and which not just by seeing the file's extension.
Given two files to us, one with a .js extension and the other with a .jsx extension, without even looking at the code, we can quickly make a judgement that the second file, i.e. the .jsx one, is meant to hold React code.
But we can't claim this for the .js file. It may or may not contain React code; we can't be sure about it.
This lack of surety is actually helpful when organizing large code bases. That is, when we know that a file contains just JavaScript code, and not any JSX, we must give it a .js extension. Otherwise, given that the file contains JSX code, we must give it a .jsx extension.
Keep in mind that this is NOT a necessity. If you feel comfortable with .js, you can continue using it to hold JSX code.