Twitter web is a complex application built with React Native for Web – an implementation of the React Native components and APIs for the browser.
React Native for Web provides cross platform primitives that normalize inconsistencies and allow to build web applications that are, among other things, touch friendly.
To the eyes of somebody who’s not familiar with the framework, the HTML produced by React Native for Web might look utterly ugly and full of bad practices.
In this blog post we will see how to make sense of this source code and why the authors made some unconventional and controversial choices.
React Native provides a small number of cross platform primitives that are the building blocks for your application. The most important ones are:
- Button - implemented with Touchable
To ensure reliable interactions like touch or gestures and to provide a higher degree of compatibility, React Native for Web reimplements some web primitives making sure that semantics and accessibility are preserved if not enhanced.
Because of this some of the primitives compile to
div, hence the div soup 🍜 that is visible when inspecting twitter.com
Let’s look at an example of React Native for Web code with three instances of Text and a Button
Most of this code compiles to divs with appropriate, though sometimes redundant, accessibility attributes:
This looks messy but let’s see what is going on.
The accessibility tree produced by our code snippet looks correct, recognising a heading, paragraph, link and a button.
The framework author has put in a lot of effort into producing advanced yet accessible components that give us better cross platform primitives.
Cross platform normalization and consistent behavior are crucial or else they’d use web native and semantic elements.
Elements made of divs
At this point you might be wondering “What’s wrong with native elements?” “Why not use web platform primitives that come with semantics and built-in behavior?”.
Why reimplement a button when the web platform provides one?
The short answer is that the button element doesn’t support flexbox children and on touch devices the implementation of the web platform button is not good enough to match the behavior of correspondent mobile native versions.
In a talk called “The Untouchable Web”, @rickhanlonii does a superb job at explaining in detail why React Native for Web implements a Button component
Why would you output a div with a paragraph role when you can use a p element instead?
The author provides a reasonable explanation in the framework docs:
The “paragraph” role isn’t mapped to a <p> tag because it’s an HTML conformance error to include block-level children within the element; in React Native for Web both Text and View support block-level children.
Hashed CSS class names
Looking at the final HTML produced by React Native for Web it is impossible not to notice the abundant amount of hashed CSS class names that goes with every element.
Let’s make sense of these classes.
The classes prefixed with
css- are for CSS rules that define base styles for the View, Text, Image and TextInput primitives.
Imagine those as User Agent styles but for the React Native primitives.
In development the prefix includes the primitive name. For example View will get
As a developer you write
<View /> and get the following HTML:
The other type of class name is the one prefixed with
r-. These are for styles authored by the consumers of the framework, for example Twitter’s engineers.
React Native for Web implements the React Native StyleSheet API and produces atomic CSS class names that are resolved deterministically in application order.
As for primitive classes, in development the prefix includes the CSS property name. For example
StyleSheet.create API which accepts an object of name-rule pairs. Developers can then reference rules by name using the dictionary returned by
Styles can be composed by passing an array of rules references to the style prop:
You can learn more about styling in React Native for Web by reading the official documentation.
How can devs make sense of all of this?
Even knowing all the implementation details mentioned above, the disconnect between source code and output can make it hard to use the browser developer tools to debug an application built with React Native for Web.
So how do developers work around this issue?
The answer is “by using more developer tools”, specifically the React Developer Tools.
The React Developer Tools have first-class support for React Native and come with a panel to inspect primitives and edit styles.
By clicking the eye icon at the top-right corner of the selected element panel, the developers tools can then help us trace down the host DOM element that React Native for Web renders.
While not being a good choice for every kind of website, React Native for Web is a framework that can help developers build better cross platform applications.
The not-so human readable source code of twitter.com is the output of a framework that provides new, cross platform primitives which overcome the limitations of similar web platform primitives.
role at divs.
Hopefully the high level explanation in this blog post can help with making sense of twitter.com’s HTML and seeing beyond the div soups and ugly CSS class names that show up in the developers tools.
I surely have been learning a lot from the new twitter.com source code and by using this knowledge I have been able to hack on the site to tweak some of its user interface and features in Refined Twitter Lite.