React over the wire

Posted 2021-01-12

"HTML over the wire" is pretty old concept from AJAX times, but it doesn't work well for native apps: browsers are still a bloated piece of software and using "just html" is not really an option for productivity or mobile tools that simply have to be very fast.

It is not the Javascript problem: JavaScript on your iPhone is ~10% faster than one in your Chrome on MacBook Pro (pre-M1) and JavaScript is just ~10% slower than Go on same MacBook in worst case scenario. Most of the speed gain in recent development of Javascript engines came from implementing some of the features natively (like promises and async functions) and reducing native-js bridge overhead. Unfortunatelly not every library can use this optimisations since they are often targeted for an obsolete JS versions, but this is not the case for React and React Native.

Biggest issue in Web development is HTML and it's rendering with a bunch of legacy implicit stuff happening. In practice ditching this legacy stuff gives you very performant apps written in JavaScript/Typescript. I have yet to find anyone to notice anything "non-native" while using Openland on iOS and on Android. (NOTE: it is easy to fuck up native look and feel of React Native apps if you came from Web where quality standarts are so low that you wouldn't see any issues that are for your users)

HTML is relatively fast on iOS, but it is not on Android. On Android it is very unpredictable and slow, it is slow on iOS too, but devs are usually don't care about users with two+ year old iOS devices and live in the bubble that browsers are fast and useful.

I am proposing the simple solution: ship React component tree instead of HTML in the form of JSON (or any other serialization format). JSON is easy to parse and since it is React you can easily implement native rendering. It turns our that react-test-renderer is a perfect for rendering components to a tree to ship them to a client.

import renderer from 'react-test-renderer';
let layout = renderer.create(
    <view style={{ backgroundColor: 'red', flexDirection: 'row' }}>
        <view style={{ flexGrow: 1, flexShrink: 1 }}>
            <text>
                Hello World!
            </text>
        </view>
        <view style={{
            width: 64,
            height: 64,
            backgroundColor: 'green',
            borderRadius: 50,
            marginHorizontal: 16
        }}/>
    <view>
).toJSON();

This will produce easy to use component tree:

{
    "type": "view",
    "props": {
        "style": {
            "backgroundColor": "red",
            "flexDirection": "row"
        }
    },
    "children": [
        {
            "type": "view",
            "props": {
                "style": {
                    "flexGrow": 1,
                    "flexShrink": 1
                }
            },
            "children": [
                {
                    "type": "text",
                    "props": {},
                    "children": [
                        "Hello World!"
                    ]
                }
            ]
        },
        {
            "type": "view",
            "props": {
                "style": {
                    "width": 64,
                    "height": 64,
                    "backgroundColor": "green",
                    "borderRadius": 50,
                    "marginLeft": 16
                }
            },
            "children": null
        }
    ]
}

Then we can render this tree back and map this to React Native views by a simple recursive rendering of this tree and just copy props as is to a components.

Everything is in Public Domain