How to Build Your Own React Library and Demystify JSX

Build Your Own React Library and Demystify JSX

Create Your Own React Library and Demystify JSX

Hey there, fellow alchemists! Welcome back to “The React Alchemist: React Essentials” series. So far, we’ve set up our React environments and peeked into how React injects itself into HTML. That was pretty cool, right? But what if I told you we could go even deeper? Today, we’re going to do something truly unique: we’re going to build our *own* mini-React library. Yes, you heard that right! We’ll craft a basic version of React’s core functionality to really understand what’s happening under the hood. This isn’t just about learning React; it’s about demystifying it. It’s about gaining that deep confidence that comes from seeing how the magic works, not just using it.

Learning Objectives

By the end of this article, you should be able to:

  • Construct a simplified version of React’s rendering mechanism.
  • Understand the internal object structure that React uses to represent UI elements.
  • Implement a custom function to render these elements into the DOM.
  • Grasp how JSX is transformed into JavaScript objects by bundlers.
  • Differentiate between an executable function and a data object in the context of React rendering.
  • Properly inject JavaScript variables and expressions into JSX.
  • Navigate and extract information from the official React source code on GitHub.

Prerequisites

Before we embark on this deep dive, make sure you’re comfortable with:

  • HTML & CSS Basics: You’ll be working with web elements.
  • Strong JavaScript Mastery: This is absolutely crucial for this session. Understanding DOM manipulation, objects, functions, and loops in JavaScript is non-negotiable.
  • Previous Articles: Having completed “Your First React Project: Setup, Structure, and Hello World” and “Deep Dive: React Project Structure and How React Injects into HTML” will provide the necessary context.

The Blueprint: How React Sees Your UI

You write JSX, which looks a lot like HTML, right? But browsers don’t understand JSX directly. So, what happens? Your React project uses a “bundler” (like Vite or Babel) to transform that JSX into regular JavaScript. And what does it transform into? An object! A plain old JavaScript object that describes your UI element. This is the core idea behind our custom React. We’re going to simulate this internal object structure.

Imagine a simple HTML `a` tag. How would React represent it internally? It might look something like this:


const reactElement = {
    type: 'a', // What kind of HTML element is it?
    props: { // What are its attributes?
        href: 'https://google.com',
        target: '_blank',
        // Any other attributes would go here
    },
    children: 'Click me to visit Google' // What text or other elements are inside it?
};
            

This `reactElement` object is the raw data that React works with. It’s not HTML; it’s a JavaScript object representation of HTML.

Building Our Custom Renderer: The Alchemist’s Touch

Now that we have our `reactElement` object, how do we get it onto the actual web page? We need a function that can take this object and turn it into a real DOM element. Let’s create a new HTML file, maybe call it `custom-react.html`, and a JavaScript file, say `custom-react.js`, to house our custom logic.

`custom-react.html` (The Basic Structure):

This will be a super simple HTML file, just like our `index.html` from a React project, with a root div and a script tag to load our custom JavaScript.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Custom React App</title>
</head>
<body>
    <div id="root"></div>
    <script src="custom-react.js"></script>
</body>
</html>
            

`custom-react.js` (Our First Renderer – Version 1):

This is where the magic begins. We’ll grab our root element and then define our `customRender` function.


// The custom react element object, as React might see it internally
const reactElement = {
    type: 'a',
    props: {
        href: 'https://google.com',
        target: '_blank',
    },
    children: 'Click me to visit Google'
};

// Get the main container from the HTML
const mainContainer = document.getElementById('root');

// Our custom rendering function
function customRender(reactElement, container) {
    // 1. Create a DOM element based on the reactElement's type
    const domElement = document.createElement(reactElement.type);

    // 2. Set the inner text (children)
    domElement.innerHTML = reactElement.children;

    // 3. Set attributes (props) manually for each known prop
    domElement.setAttribute('href', reactElement.props.href);
    domElement.setAttribute('target', reactElement.props.target);

    // 4. Append the created DOM element to the container
    container.appendChild(domElement);
}

// Call our custom render function
customRender(reactElement, mainContainer);
            

Open `custom-react.html` in your browser. You should see a clickable “Click me to visit Google” link! We just manually mimicked a tiny piece of what React does!

Improving Our Renderer: Version 2 (Looping Through Props)

The first version of `customRender` is a bit clumsy, isn’t it? What if our `reactElement` had 10 different properties? We’d have to write `domElement.setAttribute` 10 times. That’s not very scalable. A better approach is to loop through all the properties in the `props` object.

Let’s refine our `customRender` function in `custom-react.js`:


// ... (reactElement and mainContainer remain the same) ...

function customRender(reactElement, container) {
    const domElement = document.createElement(reactElement.type);
    domElement.innerHTML = reactElement.children;

    // Loop through all properties in the props object
    for (const prop in reactElement.props) {
        // A small check: in actual React, 'children' is handled separately,
        // but it might appear in props in some contexts. We'll skip it here.
        if (prop === 'children') continue; // This line is for conceptual clarity, not strictly needed for our simple reactElement

        domElement.setAttribute(prop, reactElement.props[prop]);
    }

    container.appendChild(domElement);
}

customRender(reactElement, mainContainer);
            

This version is much more robust! It can handle any number of attributes in the `props` object without needing manual updates for each one. This is closer to how a real library might handle attributes.

Exercise 4.1: Extend Your Custom React

Modify the `reactElement` object in your `custom-react.js` to represent a `div` element with an `id` of “myDiv” and some inner text like “This is my custom div!”. Ensure your `customRender` function (Version 2) still works correctly.

Solution:


const reactElement = {
    type: 'div',
    props: {
        id: 'myDiv',
        // No href or target needed for a div, our loop handles this gracefully!
    },
    children: 'This is my custom div!'
};

// ... rest of custom-react.js remains the same ...
                    

When you open `custom-react.html`, you should see the text “This is my custom div!” and if you inspect the element, it will be a `div` with `id=”myDiv”`.

Demystifying JSX: The Compiler’s Role

Okay, so we’ve seen how React *internally* represents your UI as an object. But you don’t write objects directly in your React components, do you? You write JSX, like this:


// Inside your App.jsx or App.js
function App() {
  return (
    <div>
      <h1>Hello from JSX!</h1>
    </div>
  );
}
            

How does that `

` tag inside JavaScript become our `reactElement` object?

This is where your bundler (Vite, or Babel if you were using `create-react-app`) comes in. It acts as a **transpiler**. It takes your JSX code and converts it into standard JavaScript that the browser can understand. Historically, this conversion was done using `React.createElement()` calls.

`React.createElement()`: The Official Way

Before JSX became widespread, or when you needed to create elements programmatically, React provided a function: `React.createElement()`. This function takes arguments that directly map to the properties of our `reactElement` object.

The syntax for `React.createElement()` is generally:


React.createElement(type, props, ...children);
            
  • `type`: The HTML tag name (e.g., `’div’`, `’a’`, `’h1’`) or a React component.
  • `props`: An object containing all the attributes (like `href`, `target`, `id`, `className`).
  • `…children`: Any child elements or text content. These are passed as separate arguments.

So, our original `reactElement` object would actually be created by React like this:


// This is what your JSX <a href="https://google.com" target="_blank">Click me to visit Google</a>
// would be converted to by your bundler:
const reactElementFromReact = React.createElement(
    'a', // type
    { href: 'https://google.com', target: '_blank' }, // props object
    'Click me to visit Google' // children (text content)
);
            

The beauty of JSX is that it’s just syntactic sugar over these `React.createElement()` calls. It makes writing UI much more intuitive and readable, but behind the scenes, it’s all JavaScript objects being created.

Exercise 4.2: JSX to `createElement`

Imagine you have the following JSX in your React component:


<p className="greeting">Hello, <span>World</span>!</p>
                

How would this JSX likely be transformed into `React.createElement()` calls?

Solution:


React.createElement(
    'p',
    { className: 'greeting' },
    'Hello, ',
    React.createElement('span', null, 'World'), // null for props if none
    '!'
);
                    

Notice how nested elements become nested `React.createElement` calls, and text nodes are separate children arguments.

Injecting Variables into JSX: Evaluated Expressions

One of the most powerful features of JSX is the ability to embed JavaScript directly within your UI. You do this using curly braces `{}`. But there’s a crucial distinction: you can only embed **evaluated expressions**, not full JavaScript statements like `if/else` or `for` loops.

Consider this example in your `App.jsx`:


import React from 'react';

function App() {
  const userName = "Chai and Code"; // A JavaScript variable

  return (
    <div>
      <h1>Welcome, {userName}!</h1> {/* Injecting the variable */}
      <p>The current year is {new Date().getFullYear()}.</p> {/* Injecting a function call */}
    </div>
  );
}

export default App;
            

When React renders this, `{userName}` is evaluated to its value (“Chai and Code”), and `{new Date().getFullYear()}` is evaluated to the current year. The *result* of the JavaScript expression is what gets injected into the DOM.

You *cannot* write things like this directly inside JSX:


// This will cause a SyntaxError in JSX!
<div>
  { if (isLoggedIn) { return <p>Welcome!</p>; } }
</div>
            

Why? Because the JSX is being parsed into that `React.createElement` object structure. An `if` statement or a `for` loop doesn’t fit neatly into an object’s key-value pairs or as a direct argument to `createElement`. You need to perform that logic *outside* the `return` statement in your component function, and then return the *result* of that logic into your JSX.

Journey to the Source: React’s GitHub Repository

For those of you who really want to peel back all the layers, React is open source! You can actually go and look at its source code. It might seem daunting, but it’s a fantastic way to build confidence and truly understand how things work. You can find the main React repository on GitHub.

If you dive into the `packages/react/src` directory, you’ll find files related to `React.createElement`. You might see how it internally handles different types, props, and children. It’s a complex beast, with optimizations and algorithms that make it incredibly performant, but the core idea of transforming your UI into a data structure (an object) remains consistent.

Exploring the source code isn’t for everyone, and it’s certainly not a prerequisite to using React effectively. But if you’re curious, it’s there, and it reinforces the concepts we’ve discussed today.

Conclusion

Wow, what a session! We really went deep today, didn’t we? You’ve now built a rudimentary version of React’s rendering engine, understanding how UI elements are represented as simple JavaScript objects. You’ve seen how JSX is a convenient way to write these objects, and how bundlers like Vite translate your clean JSX into the underlying `React.createElement()` calls.

We also tackled the critical concept of evaluated expressions in JSX, understanding why you can inject variables but not full JavaScript statements directly. And for the truly adventurous, we even talked about peeking into React’s actual source code. This level of understanding, knowing how things work at their fundamental level, truly empowers you. You’re not just using a library; you’re understanding its very essence. That’s the mark of a true alchemist!

Next Steps

Now that you’ve got a rock-solid understanding of React’s core mechanics and how JSX transforms, it’s time to put this knowledge to practical use. We’ll start building more complex, interactive projects, leveraging the power of React’s state management and component lifecycle. Get ready to build some truly amazing applications!

One thought on “How to Build Your Own React Library and Demystify JSX

Leave a Reply

Your email address will not be published. Required fields are marked *