Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
April 14, 2022 01:37 pm GMT

Vanilla JSX

Hi everyone! In this article I will tell how you can use JSX in a project with TypeScript and Vite bundler. This material is for you, if you:

  • Have experience with React, but no idea about how it handles JSX
  • Curious about front-end fundamentals
  • A geek who loves vanilla TypeScript and all around it

Why? For fun, of course! The idea of vanilla JSX is not suitable for a real project without over-engineering. Most likely, scaling the JSX support would cause the creation of the new frontend framework. So, open the GitHub repo in a new browser tab and make yourself comfortable. We have a deep dive into the JSX ahead!

What isJSX?

JSX is a syntactic extension over JS. It is not in the ECMAScript standards, so the tools like Babel and React deal with JSX transpiling into the plain JavaScript. Let's look at the classic JSX example:

const profile = (  <div>    <img src="avatar.png" className="profile" />    <h3>{[user.firstName, user.lastName].join(" ")}</h3>  </div>);

After the @babel/plugin-transform-react-jsx run, the code will become understandable by browsers:

const profile = React.createElement(  "div",  null,  React.createElement("img", { src: "avatar.png", className: "profile" }),  React.createElement("h3", null, [user.firstName, user.lastName].join(" ")));

As you can see, Babel successfully transformed JSX into a neat React.createElement function. It comprises the wrapper tag, its properties (or attributes, in the above case - null) and child elements, which, in their turn, are created with the same function.

React, Vue, and Solid frameworks deal with JSX by themselves, but they do it differently. This happens because they have different implementations of the createElement function. By the way, it is called JSX Pragma. When I learned about it, I immediately decided to create my own Pragma.

JSX Parsing

Before jumping into the Pragma creation, we need to learn how to parse JSX. For small modern project without old browsers support, we wouldn't need Babel, but Vite or TypeScript is enough. We will use both.

JSX Code transformation: from code to typescript parser, vite bundler, into the Vanilla JS code

Vite is a modern frontend app bundler. Compared to Webpack, it serves the source code over native ESM. To bootstrap the project, we just need to run the command:

npm create vite@latest

Vite and TypeScript, by default, parse JSX in.jsx or.tsx files. It substitues the result of parsing into the React.createElement function. Though, if we want a custom function to be substituted, we need to change the tsconfig.json.

{  "compilerOptions": {    "jsx": "preserve",    "jsxFactory": "h",    "jsxFragmentFactory": "Fragment"  }}

If you are writing the app without TypeScript, change the vite.config.js.

import { defineConfig } from 'vite';export default defineConfig({  esbuild: {    jsxFactory: 'h',    jsxFragment: 'Fragment'  }});

These settings tell the parser to use the h (meaning hyperscript, hypertext + javascript) function for JSX with only one root element and Fragment for multiple-root JSX.

JSX Pragma

After configuring the parser to handle the h function, we can start implementing it in src/pragma.ts.

// Tag can be string or a function if we parse the functional component type Tag = string | ((props: any, children: any[]) => JSX.Element);// Attributes of the element  object or nulltype Props = Record<string, string> | null;// Element children  return value from the h()type Children = (Node | string)[];export const h = (tag: Tag, props: Props, ...children: Children) => {  // If tag is a component, call it  if (typeof tag === 'function') {    return tag({ ... props }, children);  }  // Create HTML-element with given attributes  const el = document.createElement(tag);  if (props) {    Object.entries(props).forEach(([key, val]) => {      if (key === 'className') {        el.classList.add(...(val as string || '').trim().split(' '));        return;      }      el.setAttribute(key, val);    });  }  // Append child elements into the parent  children.forEach((child) => {    el.append(child);  });  return el;};

Just like createElement, the h function accepts the tag name (or a functional component), properties and results of the h function over the child elements.

All.jsx files must import the h function, so it is in the code scope after the transpilation. For example, this is a simple JSX usage.

import { h } from '../pragma';import { LikeComponent } from './like';export const App = (  <main className="hello">    <h1>      Hello JSX!    </h1>    <LikeComponent big />  </main>);

All we have to do now is add the transpiled code into the HTML:

import { App } from './components/app';const app = document.querySelector<HTMLDivElement>('#app')!app.append(App);

That's it! We've created the app in with TypeScript that parses the JSX, and Pragma that creates the correct DOM for displaying it on a website!

Practical use

As I said in the beginning, this idea is not meant for use in real projects. It only shows how easy it is to parse JSX without the use of runtime libraries, literally working in vanilla JS.

The concept of JSX Pragma is difficult to scale. If you wish to add logic to functional components, you'd have to handle the cases with variables and event listeners, thus re-implementing the concept of reactivity.

Conclusion

It turned out that such a non-standard concept as JSX Pragma can easily be handled with no frameworks, with vanilla JS only!

I encourage you to experiment with all the technologies that you can get your hands on, go as deep as possible into them. Good luck!


Original Link: https://dev.to/vanishmax/vanilla-jsx-4aa4

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To