JSX is a way to write HTML inside of JavaScript, but it feels more like a way to write JavaScript inside of HTML. As a templating language, it is beloved by some and loathed by others. Here’s a look at how it works and why it’s important.
Templating with JSX
JSX was introduced as a templating language for the wildly popular React framework. It gives you a way to define the structure of an application view with HTML markup that interacts with the application’s JavaScript context. This simple notion flies in the face of conventional wisdom about separating the view from the behavior, which is why developers tend to either love it or hate it.
Ignoring the controversy about JSX in principle, we can focus on the question of how to use it. JSX is the de facto standard for reactive templating engines, and inspires those used by others such as Vue, Svelte, and so on. Here’s what basic JSX looks like in a React application (see the live version):
import React from 'react';
export function App(props) {
return (
<div className='App'>
<h1>Greetings from InfoWorld</h1>
<h2>This is some JSX</h2>
</div>
);
}
If you look at everything inside the <div>
, you’ll see it is just HTML. It is, however, wrapped inside of JavaScript. The HTML is a return value for the App
function, which is a functional component in React. The JSX markup is the function’s return value.
In essence, the JSX return value tells the React render engine what the component’s output is.
HTML inside JavaScript
Once upon a time, it was strange to see markup inlined into JavaScript, but now it is commonplace. In fact, it is very convenient to have the markup and JavaScript together. Let’s say we want to introduce a variable into the markup. We could do it like so (see the live version):
export function App(props) {
let [name, setName] = React.useState("User");
return (
<div className='App'>
<h1>Greetings from InfoWorld</h1>
<h2>Hello {name}</h2>
</div>
);
}
Now we are using the “name
” variable inside the JSX. The name
variable is created using the React.useState
hook, but it could be any JavaScript variable as long as it is in scope. (When using functional components, the useState
hook is the correct way to use a variable in JSX.)
The curly braces around name
in the JSX template denote a JSX expression. They allow you to execute JavaScript expressions inside the markup as well as referring to variables. The JavaScript executes within the larger context of the surrounding code— that’s why you can reference the variables.
Now we start to see some of the power that has made JSX so successful. You get all the facilities of JavaScript, imported libraries like the React framework, and a complete HTML syntax that can reference these features.
Notice that JSX can use expressions, but not full JavaScript. It will output the result of the expression to the view in the place it is found in the template. Things that don’t return a value, like loops, don’t work. (This is different from some other templating tools.)
Looping
You can do many things with JSX, and one of the most important is looping. Let’s say we have an array of dog breeds in scope, and now we want to display them. Here’s how we’d do it (see the live version):
<div className='App'>
<h1>Greetings from InfoWorld</h1>
<h2></h2>
<h3>{breeds.map((breed) => {
return <li key={breed}>{breed}</li>;
})}</h3>
</div>
We use the map
function to iterate over the breeds and output the markup for each. What we end up with is HTML/JSX inside of JavaScript, inside of HTML/JSX, inside of JavaScript!
We could reduce the amount of code by eliminating the return statement, like so:
<h3>{breeds.map((breed) => <li key={breed}>{breed}</li> )}</h3></code>
Keep in mind you can use the other functional methods like filter
and reduce
to output collections as loops. That gives you some power when handling loops. You can also always go into the JavaScript in the component itself to modify the data variables and then expose that to the JSX if necessary. (You can even compose the HTML inside the JavaScript and display that directly in the JSX.)
Conditionals
Another key capability is dealing with conditional control flow like if/then/else
. For example, what if, when looping over our dog breeds, we want to check on a condition like the existence of a breedOrigin
field?
Based on our setup so far, we could do this (see the live version):
<h3>{breeds.map((breed) =>
<li key={breed.name}>{
breed.breedInfo ? breed.name + ": " + breed.breedInfo : breed.name}</li> )}</h3>
Here we are using a ternary operator (the X ? Y : Z
syntax, which says, if X, then Y, otherwise, Z). This is commonly used to make if/then/else
decisions inside a JSX expression.
Another way to approach conditional rendering is to use a test case to only render the markup if the test succeeds. For example, if we want to only render the list if the array has elements (a common scenario when loading data from a remote API), we could do this (see the live version):
<div className='App'>
<h1>Greetings from InfoWorld</h1>
<h2></h2>
{ breeds.length > 0 && <>
<h3>{breeds.map((breed) => <li key={breed.name}>{breed.breedInfo ? breed.name + ": " + breed.breedInfo : breed.name}</li> )}</h3>
</>
}
</div>
If you set the breeds
variable to be an empty array, the JSX will render nothing.
Fragments
You’ll notice the empty element tags: <>
and </>
. These are React fragments, which are supported by JSX. We could have used a <div>
but <>
is more idiomatic. Also, fragments allow you to wrap many elements in JSX without creating a non-semantic wrapper element.
Events
The next essential JSX ability to know about is event handling. For example, say we want users to be able to click on a breed and open the Wikipedia page for that breed. You could do something like this (see the live version):
let [breeds, setBreeds] = React.useState([
{name:'Shih Tzu',breedInfo:'Pekanese and Lhasa Apso cross',link:'https://en.wikipedia.org/wiki/Shih_Tzu'},
{name:'Labradoodle', link:'https://en.wikipedia.org/wiki/Labradoodle'},
{name:'Vizla',breedInfo:'Hungarian breed'},
{name:'Catahoula'}
]);
const handleBreedClick = (wikiLink) => {
window.open(wikiLink, '_blank');
};
return (
<div className='App'>
<h1>Greetings from InfoWorld</h1>
<h2></h2>
{ breeds.length > 0 && <>
<h3>
{breeds.map((breed) =>
<li key={breed.name} onClick={() => handleBreedClick(breed.link)}>{breed.breedInfo ? breed.name + ": " + breed.breedInfo : breed.name}
</li>
)}
</h3>
</>
}
</div>
);
Here, we define a handleBreedClick
function to respond to the event. It just opens the wikilink in a new window. To send the event, we use a JSX onClick
handler: onClick={() => handleBreedClick(breed.link)}
. You’ll notice this is just like a normal event HTML handler, except it is in camel case (onClick
) instead of all lowercase (onclick
).
You can also define inline event handlers. For example, this will open an alert when clicked: <li onClick={() => { alert(breed.name)}} />
.
In general, you can use JSX expressions in curly braces to provide values for properties (props) on HTML elements.
Styling
JSX elements also support CSS styles. You can do this by reference or inline, as with events. Here’s an example of the former (see the live version):
const listItemStyle = {
cursor: 'pointer',
margin: '10px 0',
padding: '5px',
backgroundColor: '#f5f5f5',
border: '1px solid #ccc',
borderRadius: '5px',
};
// … same
<li style={listItemStyle} ... </li>
Just like with events, we define a variable in JavaScript and then reference it in the property. In this case, we use the style
property and provide it with a JavaScript object. The object is expected to be a set of key values where the key is the CSS property name and the value is the CSS value string. The CSS properties use camelCase instead of the dashed names found in CSS. (This is to get around JavaScript’s naming limitations.) So, background-color
becomes backgroundColor
.
To use an inline style, you use the double-curly brace format, which looks strange but basically says, here is the style and here is a JavaScript object to satisfy it (see the live version):
<li style={{backgroundColor:'#f5f5f5',padding:'5px'}} …>
Error handling
A natural question is how to deal with errors in JSX, but this becomes a broader question because JSX is part of React. You can learn more about React and JSX error handling here, including how to use ErrorBoundary
components to wrap error segments.
Conclusion
You’ve had a look at the basics of JSX. These basics give you most of the power required for building user interfaces. The beauty of JSX is that it just extends two familiar technologies—JavaScript and HTML—and blends them into a natural whole. The three tools together have a great deal of synergy.
Once you understand JSX, you can readily carry it over to other Reactive frameworks and their templating languages, like Vue, Angular, and Svelte. There are always little idiosyncrasies, and JSX is a useful baseline to return to in comparison learning and exploration.