I have personally held the distaste of seeing JSX being used in React. For years, I had very strong opinions against using it until I had no other choice but to learn it.
I want to share 3 misconceptions I had and how I realized that these things were not entirely true when you look at it from a certain point of view.
You're going to find that many of the truths we cling to depend greatly on our own point of view - Obiwan Kenobi
I will not try to preach that JSX has without any flaws. As with other tools, a tool is only as good as how you would use it.
JSX is now being used even outside of React, on projects like MDX, VueJS, StencilJS, SolidJS, Mitosis, AtomicoJS and Preact. In this blog, I will be focusing on the context of React to explain my thoughts regarding JSX.
No Separation of Concerns
JSX forces us to mix JavaScript and HTML together. This means that it is trying to force us to go against the mantra of Separation of Concerns due to the mixing of layout & logic.
Here’s the thing, using JSX to write your presentation code doesn’t necessarily mean giving up these principles. Let us look at this very simple component in React
const HelloWorld = ({name}) => {
const message = `Hello ${name}`;
return <h1>{message}</h1>
}
There’s no reason why that separation can’t occur in the context of JavaScript. In this example the layout & logic is separated like this:
const HelloWorld = ({name}) => {
// component logic is confined above the JSX block
// some logic to form message
const message = `Hello ${name}`;
// JSX is confined to this return block
return <h1>{message}</h1>
}
One important thing to note is that separation of concerns is not equal to separation of file types. In modern UI development, we have found that instead of dividing the codebase into three huge layers that interweave with one another, it makes much more sense to divide them into loosely-coupled components and compose them. Inside a component, its template, logic and styles are inherently coupled, and collocating them actually makes the component more cohesive and maintainable. - VueJS Documentation
To better understand this, let us now try converting this component into an Angular component.
@Component({
selector: 'hello-world',
// template is confined in this property
template: `<h1>{{message}}</h1>`
})
export class HelloWorldComponent {
// component logic is confined in this class
message: String;
@Input()
public set name(name: string) {
// some logic to form message
this.message = `Hello {name}`;
}
}
On Angular, you can separate the template using files. I will be using MVVM/MVC terms to properly explain what is going on here.
Component Controller
@Component({
selector: 'hello-world',
templateUrl: './hello-world.html',
})
...
Component View
<h1>{{message}}</h1>
You may say that the template/layout example above is so simple so it doesn't really show the dark side of JSX. We could switch it up a bit by throwing it in a condition.
JSX
{render && <h1>{message}</h1>}
Angular Template
<h1 *ngIf="render">{{message}}</h1>
As you can see above they are just about the same, JSX using JavaScript & HTML, while Angular introducing *ngIf
.
In this case, the conditional logic is actually mixed with the layout on both examples. Be it separated by JavaScript context in React, or separated by files in Angular.
In React, the layout is in the same file as the component. This gives you a clear idea about the component context and what is available at your disposal. However, you will likely have a very long file as time goes by even in Vue SFCs. Luckily, editors nowadays have a way to split the window so that you can work on different sections of the same file.
Even though I have stated here that you can confine JSX to the return
part of the component. There is definitely no one stopping you from doing this.
const HelloWorld = ({name}) => {
const message = <h1>{`Hello ${name}`}</h1>;
return message;
}
If you have to declare multiple JSX pieces across your component, think of a better way to achieve this using multiple components.
Another Templating Syntax
While JSX is claiming that it's easy to learn it because you can write HTML on JavaScript, we can think that it is actually an abomination of what we have been trying to achieve separating HTML and JavaScript.
On common frontend libraries/frameworks we have actually come up with different solutions to keep the separation between HTML from JavaScript while making HTML dynamic: Angular Template Syntax, VueJS Template Syntax.
Did you notice that there is no such thing as a JSX Template Syntax or a React Template Syntax? This is probably the only page which explains JSX and it's just a very short documentation. Though it looks like it, JSX isn’t a templating engine, in the sense that Handlebars is a templating engine, similar to what I have linked above.
JSX is actually more than that, it is a syntax extension for JavaScript. Basically, it extends JavaScript so that HTML/XML like structures can be used with JavaScript.
If you actually looked at the documentation I linked above, there is a lot to learn to get started with these templating engines as compared to JSX. In JSX, at the bare minimum, you only need to know JavaScript and HTML and a few extra exceptions. Below is an example of rendering items using a loop in JSX.
<div>
{names.map(name => (
<li>
{name}
</li>
))}
</div>
Looking at this you only need to know what the JavaScript .map
does, HTML div
and li
and the extra {}
for evaluating JavaScript code. I know, this does not look clean at first, it looks like an ugly soup of JS mashed with HTML. But, let me walk you through what is happening here.
<div>{}</div>
In JSX, HTML and JS is considered a valid JSX. However, to slap JS within an HTML you need to make use of {}
. Now let us look inside the {}
.
names.map(name => (<li>{name}</li>))
This is JavaScript returning a collection of <li/>
which is an HTML but is also considered valid JavaScript in JSX.
While we can think that React and JSX go against how templates are done in MVVM/MVC frameworks, that is not entirely true. Here are some templating engines on popular backend frameworks that would look very familiar to you.
Blade Templates (PHP)
<div>
@foreach ($name as $names)
<li>{{ $name }}</li>
@endforeach
</div>
Razor Templates (C#)
<div>
@foreach (var name in names)
{
<li>@name</li>
}
</div>
Jinja Templates (Python)
<div>
{% for name in names %}
<li>{{name}}</li>
{% endfor %}
</div>
As you can see this is about the same concept in PHP, C#, Python templating engines. You just have to know the language and some extras to actually remember it.
Now let us go back to the frontend. Let us look at the template engine of Handlebars, VueJS, Angular, and see how looping is implemented.
Handlebars
<div>
{{#each names}}
<li>{{this}}</li>
{{/each}}
</div>
VueJS
<div>
<li v-for="name in names">{{name}}<li>
</div>
Angular
<div>
<li *ngFor="let name of names">{{ hero }}</li>
</div>
I must admit, these look cleaner in my opinion. But, I think once the props or the attributes get into a certain number, it will be harder to identify the scope and context of control structures. You also have to remember a lot of custom attributes. Other than that, it just takes getting used to.
To drive my point home, JSX is not a templating engine but it allows us to do what another templating engine has been doing for years. On top of that, it looks very familiar since you are dealing with JavaScript and HTML.
There are more uses to JSX other than conjuring templates, you can visit the list of links I mentioned in the introduction of this article. If you don't like React, you could still consider using JSX from those projects.
Personally, it was not React that got me started to like JSX. It was StencilJS. When I finally got the hang of it, I finally considered giving React a second chance.
Too Flexible
I agree that this could be a valid rant. As with things that are too flexible, it could easily be abused. On JSX its not only JavaScript and HTML that you should look out for. It can also be used with React components which would make it confusing for someone who doesn't know React.
A React element in JSX is also a function. Hence, JSX can be used to create cryptic code such as this
You wouldn't probably be doing this although there could be some use cases for this. While I don't have the actual implementation of this code, this could be using React Context
and Render Props
under the hood which is specifically what React provides.
If you are looking into JSX, it's highly likely you are exploring React and wonder what Render Props
are. I don't want to turn this into a React course but would just try to make a point by showing how Render Props
work.
I want to create a generic Mouse
component that outputs the x and y coordinates of the mouse within the component. I can then display the x and y coordinate in whatever way I like. So this is how we do it in React using Render Props
.
Mouse Component (React)
const Mouse = ({children, onMouseMove}) => {
const [coords, setCoords] = useState({x:0,y:0});
const onMouseMove = (event) {
setCoords({x: event.clientX, y: event.clientY});
}
return (<div onMouseMove={onMouseMove}>
children({
x: coords.x,
y: coords.y
})
</div>)
}
Use Mouse Component (React)
<Mouse>
({x,y}) => {
return <h1>The mouse position is ({x}, {y})</h1>
}
</Mouse>
In this example, we can see that the Mouse
component accepts a function and it invokes that function on its JSX block. The function which is passed also returns a JSX.
This is somewhat similar to how high order functions work in JavaScript. A higher-order function is a function that takes a function as an argument or returns a function
Let us now try to do this in Angular.
Mouse Component (Angular)
@Component({
selector: 'mouse',
template: `
<div (mousemove)="handleMouseMove($event)">
<ng-container *ngTemplateOutlet="template; context: coords" ></ng-container>
</div>
`,
})
export class Mouse {
@ContentChild(TemplateRef) template;
coords = { x: 0, y: 0 }
handleMouseMove(event) {
this.coords = {
x: event.clientX,
y: event.clientY
}
}
}
Use Mouse Component (Angular)
<mouse>
<ng-template let-x="x" let-y="y">
<h1>The mouse position is ({{x}}, {{y}})</h1>
</ng-template>
</mouse>
I can actually say that this is much harder to understand if I don't know what ng-template
, ng-container
, and TempalateRef
are and how they actually work. With JSX, we were able to achieve what we like with shorter succinct code.
Conclusion
There is actually nothing compelling about what I’ve done here — several technologies have been using some sort of templating engine for quite some time. I still like working with templates even though I have been working with JSX nowadays.
Maybe we should start using something for a considerable amount of time before actually hating it. The use of templates vs JSX, highly depends on what application you are building, your team’s preferences, experience, or perhaps whether or not you have code-capable designers on your team.
What do you like or don't like about JSX? Please share your thoughts.
Top comments (6)
JSX is the best thing happening too me since starting my software career :)
While those are probably good misconceptions to talk about, my complaints about JSX center more about what it forces you to do and how it impacts the code:
JSX ties you to forever needing to run your code through some form of compile/transpile cycle. At this point in time, every browser supports ES6/7/8/etc. and supports Web Components. Thus the need for the build cycles we've had in the past has declined to the point where it's an antipattern rather than something we want to keep doing. But JSX isn't JavaScript so as long as you use it, you're always going to have to have that extra step in your development cycle even if you really don't need it.
And it's not like React + JSX is a more performant solution than the alternatives to make up for it. In actuality, it's slower and more poorly supported. Web Components perform better on mobile browsers (they're not written in JS, they're written into the browser itself and much faster) and on the desktop too (see github.com/krausest/js-framework-b... and trim down the frameworks to something managable like lit-html, litelement, react, angular, and a few other of your favorites). You'll likely find that the lit based ones are the fastest, use the least memory, etc.).
JSX is technically a templating language so it's not truly tied to React, but that's kind of like how React "isn't tied to JSX". Yeah, you can use React without using JSX. Do you know anybody doing that? No? I didn't think so. But is React in general doing things we actually need done anymore? React is giving us a component model. It's not one supported directly by the browser, it's one which doesn't make use of the Shadow DOM (you can add on CSS-in-JS solutions, but those are not the same), and including a new component is hardly as easy as including a JS file and just typing in the HTML.
So why do we need it? Why include something that makes us have a build in our development cycle and is slower to boot? If you are actually doing server-side rendering, sure, I salute you for doing that and you've got something that is not well supported in Web Components today. Otherwise? Ask yourself why you're signing up for this.
I actually agree with your points here that's why these things are not listed as misconceptions.
You can actually say this about TypeScript as well and other toolings that actually require a build. I don't have a strong opinion about this. We can either innovate towards making tooling drop the requirement of builds altogether or we can just make it faster.
esbuild is 100x faster when compared to webpack and rollup
evanw / esbuild
An extremely fast JavaScript bundler and minifier
esbuild
This is a JavaScript bundler and minifier. It packages up JavaScript and TypeScript code for distribution on the web.
Why?
Why build another JavaScript build tool? The current build tools for the web are at least an order of magnitude slower than they should be. I'm hoping that this project serves as an "existence proof" that our JavaScript tooling can be much, much faster.
Benchmarks
The use case I have in mind is packaging a large codebase for production. This includes minifying the code, which reduces network transfer time, and producing source maps, which are important for debugging errors in production. Ideally the build tool should also build quickly without having to warm up a cache first.
I currently have two benchmarks that I'm using to measure esbuild performance. For these benchmarks, esbuild is at least 100x faster than the other JavaScript bundlers I tested:
Here are the details…
Personally, this is a demo project I created which showcases the use of web components, hooks, css-in-js, context, routing without any build step:
geocine / web-components-hooks-demo
web components hooks demo
Web Components + Hooks Demo
This demonstrates a basic shopping cart built using Web Components. It also has State Management, Routing, CSS-in-JS and does not need a build step.
Technologies used:
Usage
This demo runs without a build step.
Development
Just run on a webserver with
index.html
as a fallback page. I suggest using lite-server on root directory. Theyarn start
script useslite-server
.If you are serving this on a different
host:port
make sure to replace all instances ofI am also not entirely naive about Web Components. I have created a compiler to enable you to create native custom elements using Typescript
geocine / custom-elements-ts
Create native custom elements using Typescript
custom-elements-ts
Create native custom elements using Typescript without using any third party libraries.
Usage
Decorators
.emit
method of its typeDispatchEmitter
. Theevent
parameter is used to set the name of theI think it is dangerous to be married against one side of the camp. Ionic is one of the companies I know that heavily promotes Web Components. They actually created a compiler which would help them build Web Components efficiently as compared to the "low level API" that custom elements provides. Hence, StencilJS was born.
ionic-team / stencil
A Web Component compiler for building fast, reusable UI components and Progressive Web Apps 💎 Built by the Ionic Framework team
Stencil: A Compiler for Web Components and PWAs
Stencil is a simple compiler for generating Web Components and progressive web apps (PWA). Stencil was built by the Ionic Framework team for its next generation of performant mobile and desktop Web Components.
Stencil combines the best concepts of the most popular frontend frameworks into a compile-time rather than run-time tool. It takes TypeScript, JSX, a tiny virtual DOM layer, efficient one-way data binding, an asynchronous rendering pipeline (similar to React Fiber), and lazy-loading out of the box, and generates 100% standards-based Web Components that run on both modern browsers and legacy browsers back to Internet Explorer 11.
Stencil components are just Web Components, so they work in any major framework or with no framework at all. In many cases, Stencil can be used as a drop in replacement for traditional frontend frameworks given the capabilities now available in…
Ionic first focused on Angular but later realized that they should allow the use of Ionic no matter your preferred framework. They actually never took the stance against any type of framework. I think that is the same stand that I will take. You should use a technology that works for you and brings value to your business. Just like Ionic Angular, there is now the React and Vue versions, Ionic Vue and Ionic React and soon more resources and guides are going to be available to work with them. This was made possible with Web Components and the best things they learned out of the other frameworks. To me using any framework is not really an all or nothing.
I'll learn more about esbuild, I'm not familiar with it. You make an excellent point about looking at it from multiple standpoints though. There's the speed aspect of it, "If it were just fast enough not to impede my development cycle would I then care about whether there is a build step or not?"
And I think that for me the answer is, "Yes, kinda..." It's because I've done videos for commercial courses and for YouTube before as well as mentoring some at work and I think that we need to realize there are costs associated with build tools like webpack and Gulp beyond just speed. They become just another speed bump for people who want to develop but get to start with tooling instead. Alternatively they can simply adopt a "CLI" which rolls up a lot of stuff into a huge blob that you can hope never breaks and you never run off the edge of. There is really something to be said for simplicity sometimes.
However with that said, I've used custom elements with only Vanilla JS and then built the exact same components with LitElement (a small amount of additional code to handle templates, attributes, etc.) and I can say I greatly prefer the latter. Stencil, SkateJS, etc. are all just doing variations of that same thing. In fact I really like that variety and I like that JSX is one of those options because it will make a much easier transition for some developers in the future. So sometimes you can absolutely simplify and cut away too much.
I agree with this 100% , I haven't done any professional kind of teaching but I love helping others get into web development. This is really a pain, I miss the days where we can just add a script tag and that's it. I think by default libraries/framework must at least provide this option to reduce the technical barrier of learning introduced by build tools. I am glad that the JavaScript community is seeing this a legit problem to solve. Now we have things like ParcelJS and Snowpack. Still it would be a chore to explain why do we even need these, why do we even need to install NodeJS etc.
I personally prefer DOM events as compared to React's Synthetic events. I think right now though, Web Components are good enough when you are developing the individual components of the application. But If you need to create an entire application with templating, state management, routing , definitely need some help from our other friends on top of the VanillaJS Web Components.
Some comments may only be visible to logged-in visitors. Sign in to view all comments.