Motivation
React is a great framework for many reasons, but one that draws many people in is its flexibility. React is not just limited to web development; it can also be used to build native apps (opens in a new tab) and even command-line apps (opens in a new tab)!
The idea of sharing code betwen web and native is very exciting! However, if you're a web developer used to DOM elements, building apps with only View
and Text
components from React Native for Web (opens in a new tab) (or Stack
and Text
from Tamagui (opens in a new tab)) may feel foreign. That's because these tools focus on sharing native conventions with the web, not the other way around.
This has been done with good reason: React Native is not React DOM. Let's look at a few examples:
- On the web, you can simply enable scrolling by setting
overflow: auto
via CSS. On native, you have to wrap the scrollable components with aScrollView
. - On the web, linking to different pages is done with an
a
element and anhref
attribute. On native, there is no semantic difference between a link and a button — they're both justPressable
s with a different callback. - On the web, gradients can be rendered on any element via CSS. On native, you have to use an external library like react-native-linear-gradient (opens in a new tab) to render gradients via the
LinearGradient
component.
And so, these libraries have opted to prioritize native over web conventions. However, every decision comes at a cost and, as we see it, there is one main pillar that has been affected: accessibility.
Accessibility
Accessibility is baked into the web. Semantic elements (like h1
, button
, and header
) inform screen readers how to read the DOM and navigate a page. ARIA attributes (opens in a new tab) provide additional information to screen readers to build complex interactive components not native to the web, like modal dialogs, tabs, and context menus.
React Native, on the other hand, does not have semantic elements (beyond select components like Text
, TextInput
, and Image
). Screen reader interactivity is defined via roles and accessibility props (opens in a new tab), which are inspired by (but not 1:1 to) ARIA attributes.
How have other libraries tried to bridge the gap?
- React Native for Web defines accessibility information via props. The
role
prop is used to infer the correct tag for each elements, whilearia-*
props (opens in a new tab) map 1:1 to React DOM and to analagousaccessibility*
props on React Native. While this is a fine approach, inferring the tag based on role is fragile; why not just let engineers choose the tag directly? - Tamagui does something similar, but has a more explicit approach. Every component exposes a
tag
prop that lets you define the exact semantic element to use on the web. While this solves React Native for Web's issue, it introduces a new problem: the use of a prop makes the semantic meaning of the component a variable selection and not inherent to the component itself.