DEV Community

Cover image for 3 Common Misconceptions about JSX
Aivan Monceller
Aivan Monceller

Posted on • Updated on

3 Common Misconceptions about JSX

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>
}
Enter fullscreen mode Exit fullscreen mode

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>
}
Enter fullscreen mode Exit fullscreen mode

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}`;
  }

}
Enter fullscreen mode Exit fullscreen mode

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',
})
...
Enter fullscreen mode Exit fullscreen mode

Component View

<h1>{{message}}</h1>
Enter fullscreen mode Exit fullscreen mode

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>}
Enter fullscreen mode Exit fullscreen mode

Angular Template

<h1 *ngIf="render">{{message}}</h1>
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>))
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

Razor Templates (C#)

<div>
@foreach (var name in names)
{
  <li>@name</li>
}
</div>
Enter fullscreen mode Exit fullscreen mode

Jinja Templates (Python)

<div>
{% for name in names %}
  <li>{{name}}</li>
{% endfor %}
</div>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

VueJS

<div>
  <li v-for="name in names">{{name}}<li>
</div>
Enter fullscreen mode Exit fullscreen mode

Angular

<div>
  <li *ngFor="let name of names">{{ hero }}</li>
</div>
Enter fullscreen mode Exit fullscreen mode

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>)
}
Enter fullscreen mode Exit fullscreen mode

Use Mouse Component (React)

<Mouse>
  ({x,y}) => {
     return <h1>The mouse position is ({x}, {y})</h1>
  }
</Mouse>
Enter fullscreen mode Exit fullscreen mode

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
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Use Mouse Component (Angular)

<mouse>
  <ng-template let-x="x" let-y="y">
     <h1>The mouse position is ({{x}}, {{y}})</h1>
  </ng-template>
</mouse>
Enter fullscreen mode Exit fullscreen mode

I can actually say that this is much harder to understand if I don't know what ng-template, ng-container, and TempalateRefare 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.

Latest comments (6)

Collapse
 
johnmunsch profile image
John Munsch

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:

  1. 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.).

  2. 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.

Collapse
 
geocine profile image
Aivan Monceller • Edited

I actually agree with your points here that's why these things are not listed as misconceptions.

JSX ties you to forever needing to run your code through some form of compile/transpile cycle

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

GitHub logo 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:

GitHub logo 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. The yarn start script uses lite-server.

yarn
yarn start

If you are serving this on a different host:port make sure to replace all instances of

http://localhost:3000

I am also not entirely naive about Web Components. I have created a compiler to enable you to create native custom elements using Typescript

GitHub logo geocine / custom-elements-ts

Create native custom elements using Typescript

custom-elements-ts

Coverage Status Build Status npm version License: MIT

Create native custom elements using Typescript without using any third party libraries.

npm install custom-elements-ts

Usage

import { CustomElement } from 'custom-elements-ts';
@CustomElement({
  tag: 'counter-element',
  templateUrl: 'counter-element.html',
  styleUrl: 'counter-element.scss'
})
export class CounterElement extends HTMLElement {
  // code as you would when creating a native HTMLElement
  // full source code is at demo/counter
}
<!--index.html-->
<counter-element></counter-element>
<script src="counter.umd.js"></script>

Decorators

Decorator Target Parameters Description
@Prop() property - custom attribute/properties, reflects primitive properties to attributes
@Toggle() property - boolean attribute/properties, it is based on the presence of the attribute but also works with "true" and "false"
@Dispatch() property (event?) used to declare a CustomEvent which you could dispatch using the .emit method of its type DispatchEmitter. The event parameter is used to set the name of the

I 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.

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.

GitHub logo ionic-team / stencil

A Web Component compiler for building fast, reusable UI components and Progressive Web Apps 💎 Built by the Ionic Framework team

npm Build & Test license

Stencil: A Compiler for Web Components and PWAs

npm init stencil

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.

Collapse
 
johnmunsch profile image
John Munsch

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.

Thread Thread
 
geocine profile image
Aivan Monceller • Edited

They become just another speed bump for people who want to develop but get to start with tooling instead.

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.

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.

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.

Collapse
 
richardlindhout profile image
Richard Lindhout

JSX is the best thing happening too me since starting my software career :)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.