This content originally appeared on Telerik Blogs and was authored by Jonathan Gamble
JSX solves a lot of problems in React frameworks. See how it compares to template syntax for various frameworks.
TL;DR
JSX is not going anywhere and is currently winning by a long shot as the default template syntax. It also seems like some of the biggest problems with JSX won’t ever be fixed. However, React just made the workarounds the default, and many people don’t mind. Template Syntax can only win when the frameworks that support them get bigger adoption.
My Journey Recap
Perl
I started my website development journey with jQuery and Perl—yes, Perl. This was pre-PHP. Perl has an online package directory called CPAN, a precursor to npm. Many packages, like Template Toolkit, create HTML templates that are read by Perl or CGI files, and I preferred to separate my core Perl code from my HTML.
<!DOCTYPE html>
<html>
<head>
<title>[% title %]</title>
</head>
<body>
<h1>[% title %]</h1>
<p>[% message %]</p>
</body>
</html>
PHP
I always hated PHP, as you’re mixing HTML and a server-side language. To most Perl developers, this was gross. My HTML and HTML templates should be in an HTML file, my JS in a JS file, and my server code should stay on the server. Today, I wouldn’t mind PHP if Server JS Runtimes weren’t much easier.
<!DOCTYPE html>
<html>
<head>
<title><?php echo $title; ?></title>
</head>
<body>
<h1><?php echo $title; ?></h1>
<p><?php echo $message; ?></p>
</body>
</html>
Angular
I started using Angular right about when Angular 2 was released. I love the separation of concerns for templating. Using standalone components, you can put your template and JS in one file, but the syntax still needs improvement. Regardless, JS and templating are still separate things.
import { Component } from '@angular/core';
@Component({
selector: 'app-standalone-component',
template: `
<div>
<h1>{{ title }}</h1>
@if (message) {
<p>{{ message }}</p>
}
</div>
`,
standalone: true,
})
export class StandaloneComponent {
title = 'Hello World';
message = 'This is a message from a standalone Angular component!';
}
Svelte
I mostly build in Svelte, which is the simplest interactive framework. There is one file, everything is separate and it is extremely easy to use.
<script lang="ts>
let title = 'Hello World';
let message = 'This is a message from a Svelte component!';
</script>
<div>
<h1>{title}</h1>
{#if message}
<p>{message}</p>
{/if}
</div>
Nuxt
I would build with Vue and Nuxt if I didn’t use Svelte. For example sake, here is a Vue component using the latest SFC <script setup>
for local component registration. It is as simple as Svelte.
<script setup lang="ts">
const title: string = 'Hello World';
const message: string = 'This is a message from a standalone Nuxt.js component!';
</script>
<template>
<div>
<h1>{{ title }}</h1>
<p v-if="message">{{ message }}</p>
</div>
</template>
I also added a conditional so you can see how easy it is.
Enter JSX
I first used React when functional components were released with the Hooks API around version 16. Fireship had begun shifting the majority of his videos from Angular to React. Up until that point, I always thought React was about as ugly as Ember. Next.js and Vercel blew up the ecosystem with new server-side rendering (SSR) techniques.
import { FC } from 'react';
interface GreetingProps {
name: string;
}
const GreetingComponent: FC<GreetingProps> = ({ name }) => {
return <h1>Hello, {name}!</h1>;
};
export default GreetingComponent;
JSX Frameworks
JSX solves so many problems. Using children, you can pass components within components, and you don’t have to generate an HTML tag for each component as Angular does. All the class nonsense is skipped, and you just declare variables and return a JSX string. Done.
React Frameworks
- Next.js – Built by Vercel for server-side features.
- Remix (React Router) – One framework merged from two for server-side features and routing.
- Preact – Technically not React, but compatible with React components for a performance boost and a thinner package.
SolidJS
SolidJS tries to solve the problems of React, while sticking with JSX. It has gotten rid of the bloat that the core React code includes and uses Signals and Suspense by default. However, as we shall see, it also adds new official components to deal with more intuitive JSX syntax problems.
Qwik
Created by one of the core Angular engineers, Qwik handles SSR interactivity with resumablity and interactivity with Signals. I believe JSX was chosen due to the complexities of Angular classes. JSX is, by design, simple.
Problems with JSX
1. Conditionals and Loops
I don’t like JSX because of how you’re supposed to handle conditionals and loops.
// conditional
return (
<li className="item">
{name} {isPacked && '✔'}
</li>
);
...
// loop
const listItems = chemists.map(person => { // Curly brace
return <li>...</li>;
});
You see these odd &&
conditionals, ternary operators or maps
to handle basic loops. This is not a low-level programming language, I prefer ternary operators outside my template code, and maps are actually slower than for loops.
2. Return One Item
You can only return one JSX item, otherwise you have to manually group them and combine strings. You can see how this would get complex using conditionals.
import { FC } from 'react';
const CombinedComponent: FC = () => {
const elementOne = <h1>Hello, World!</h1>;
const elementTwo = <p>This is a paragraph element.</p>;
return (
<div>
<div>{elementOne}</div>
<div>{elementTwo}</div>
</div>
);
};
export default CombinedComponent;
3. Helper Components
This is also why mini-components are so prevalent; they solve several problems simultaneously. I don’t like having more than one component in a file, but I do follow the Single Responsibility Principle—your code blocks should be separated into small blocks with one purpose.
const Component1 = () => (
<div>
<h1>Hello, World!</h1>
<p>This is the first JSX component.</p>
</div>
);
const Component2 = () => (
<div>
<h2>Welcome to the App</h2>
<p>This is the second JSX component.</p>
</div>
);
const CombinedComponent = () => (
<div>
<Component1 />
<Component2 />
</div>
);
return default CombinedComponent;
While they are definitely a plus, these utility components may not be necessary in template frameworks.
SolidJS Solution
By default, SolidJS comes with built-in components to solve this problem.
Conditionals
import { Show } from "solid-js"
<Show when={data.loading}>
<div>Loading...</div>
<Show when={data.error}>
<div>Error: {data.error}</div>
</Show>
</Show>
This looks nice. However, in practice, you end up having code that looks like this:
const [user] = useUser();
return (
<Show when={user.data}>
{(user) => (
<div class="flex flex-col gap-3 items-center">
...
</div>
)}
</Show>
Loops
The loops are just as funky.
<For each={data()}>
{(item, index) => (
<li
style={{
color: index() % 2 === 0 ? "red" : "blue"
}}
>
{item.name}
</li>
)}
</For>
After building my SolidJS Firebase App, I realized why this is. You are usually looping through interactive data or data created by a Signal, which requires passing an instance of the object to the child component. SolidJS does not compile code as Svelte does.
Qwik Flow
I helped build a Community Qwik version for the Qwikifiers package called Qwik Flow. Although Qwik officially recognizes it, it has not gained traction; I don’t think the Qwik team has it on their priority list. You can read about the decisions in the Built-in Conditionals post. PRs welcome!
React Version
You could easily build your own React versions. However, when trying to localize change detection, it may not work as expected in all situations. Now that React is compiled, this may be fixed.
Conditionals
import { FC, ReactNode } from 'react';
interface ShowProps {
when: boolean;
children: ReactNode;
}
const Show: FC<ShowProps> = ({ when, children }) => {
return when ? <>{children}</> : null;
};
export default Show;
Loops
import { FC, ReactNode, Fragment} from 'react';
interface ForProps<T> {
each: T[];
children: (item: T, index: number) => ReactNode;
}
const For = <T,>({ each, children }: ForProps<T>) => {
return (
<>
{each.map((item, index) => (
<Fragment key={index}>
{children(item, index)}
</Fragment>
))}
</>
);
};
export default For;
// Usage is just as complex...
return (
<div>
<For each={items}>
{(item, index) => (
<p key={index}>{item}</p>
)}
</For>
</div>
);
These are basic ideas and may need to be modified for your specific React Framework.
Winner: JSX
Because React dominates the web development community, web developers have double-downed on JSX. It is a standard that you must use map
and &&
in your template to handle rendering. It is accepted in the expanded React community, and doesn’t seem to be on anyone’s radar to fix.
Why can’t we fix this with JSX2, or at least have built-in conditional and loop components in all JSX frameworks?
Loser: Templates
React is growing exponentially faster than other frameworks. Until there is a paradigm shift requiring more adoption and third-party plugins, you better get used to it. Either way, everything just compiles to JavaScript.
But this is my opinion, as the future isn’t written yet. Nothing dominates forever.
This content originally appeared on Telerik Blogs and was authored by Jonathan Gamble
Jonathan Gamble | Sciencx (2024-09-13T07:33:15+00:00) JSX vs. Template Syntax in Frameworks. Retrieved from https://www.scien.cx/2024/09/13/jsx-vs-template-syntax-in-frameworks/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.