<onWebFocus />

Knowledge is only real when shared.

JSX Rendering

March 14, 2025

How does JSX rendering work and how can it be improved.

In this post I'll explain how JSX rendering and especially rerendering in React works, what it's optimizing for and how improvements would look like.

With a new debug tool and in a series of X posts Aiden Bai has highlighted the issue of React rerendering many times even for seemingly simple interactions that shouldn't require such rerenders.

Rendering JSX with React

#1 Transpilation

Transpilation takes JSX and outputs regular JavaScript code by transforming the tags into function calls.

const MyButton = ({ title }) => <button title={title} />
const MyTitle = <div><MyButton title="Click me!" /></div>
const MyTitle = createElement('div', null, ???)

This is the only part that happens at build-time all further steps will happen in the client at runtime. JSX transpilation is built into the SWC Compiler and can be configured to match the desired function to be used.

#2 Initialize Elements

On the browser the createElement function is recursively called and a nested object structure is returned.

export function createElement(type: Type, props: Props, ...children: JSX[]) { ... }
const MyTitle = { type: 'h1', props: { bold: true, children: [ 'Hello, world!'] }}

#3 Rendering

Rendering

#4 Reconciliation

Reconciliation

Rerendering in React

If some state changes React will rerender the component and all of it's descendants.

Possible JSX Rendering Improvements

During reconciliation, React removes all elements that are no longer present and renders all new elements. Except for arrays with manually set keys, existing elements are never reused or copied. This despite browsers making it easy to duplicate or move existing DOM elements.

Don't Repeat Yourself

DRY a core programming principle is well established in React with components and higher-order components. HOCs often clog up JSX rendering and naturally have to be used in a place outside of there they actually apply. This could be fixed on the component rendering level itself using plugins.

Comparing Props

React only does a shallow compare on the props passed to a component. While with Redux itself any changes to children will propagate up to the parent, this isn't usually the case for data passed through props.

Rerendering Algorithm

A core principle for React is to rerender the whole component tree for when an element changes, even if its children arent affected at all.

If users rely less on props to pass data to components and more on state than can be tracked accross components and allows for fine-grained rerenders of individual components. Along with deep comparison this can reduce the number of necessary rerenders significantly.

Updating Text Nodes

Changing the content of a text element is a very common use case and can be done very performantly in JavaScript. Simple text also often changes, so it can make sense to connect some text based state directly to a text node, avoiding rerendering the whole component upon changes and tracking the variable separately with the text. A state primitive called Signals can already do this, however support for signals isn't baked into React and when it comes to state signals are very cumbersome and limited to simple atomic states.

function TimePassed(since: string) {
  return <p>{time.format('hh:mm:ss')}

Types (ReactNode, ReactElement, Fiber, JSX.Element)

Explain most important types and differences.

This post was largely written by prompting ChatGPT to write individual parts.