DEV Community

Cover image for 🤔🌐 React Native for web developers
Evan Bacon
Evan Bacon

Posted on

🤔🌐 React Native for web developers

In the world of web development, everything is streamlined. The concept of React Native is really appealing to a React developer on paper, but abandoning your existing knowledge of HTML and CSS in favor of primitives like View, Image, Text can be a lot to handle. But what if it didn't need to be? What if you could truly build native apps with your already existing knowledge of building websites.

TL;DR: You can now

The Issue

This article assumes you know React Native can be used to create iOS, Android, and Web apps. If you don't watch this!

Alt Text

React Native is great ... for native developers (and by extension the entire human race 😁). Instead of using Objective-C, or Java you can build your native app cross-platform with just JavaScript TypeScript! Even with the entirely original ideas of Flutter and SwiftUI from Google and Apple respectively, React Native is a no-brainer. The flexibility of JavaScript, the convenience of OTA updates, Expo as a whole. React Native has it all.

...unless you're a web developer. Then it's missing a few things.

The learning curve of React Native

The default flavor of React Native considers all platforms equally, this means the API you interface with doesn't have any platform specific references. A good example of this is linking.

Say we want to create a text link to open another website.

In the browser you simply create a link:

<a href="https://dev.to">Link</a>
Enter fullscreen mode Exit fullscreen mode

Natively you would create a generic Text element and use the Linking API to open a URL:

import { Text, Linking } from 'react-native';

function openLink() {
  Linking.openURL('https://dev.to')
}

export default () => (
  <Text onPress={openLink}>Link</Text>
)
Enter fullscreen mode Exit fullscreen mode

Now universally with Expo (iOS, Android, and Web), you'd do this:

import { Text, Platform, Linking } from 'react-native';

const Link = ({ href, ...props }) => (
  <Text
    {...props}
    accessibilityRole="link"
    href={href}
    onPress={() => {
      if (Platform.OS !== 'web') 
        Linking.openURL(href);
    }}
  />
);

export default () => (
  <Link href="https://dev.to">Link</Link>
)
Enter fullscreen mode Exit fullscreen mode

This is pretty unintuitive if you're coming from a web development background. For the sake of brevity I won't get into how much of nightmare it is to use this with TypeScript. href isn't in the TypeScript definition for <Text /> because web support is an out-of-tree solution. If you want to add TypeScript support you'd have to remap the types of the Text element which takes a lot of digging to get right.

Problem 002

Every front end developer talks about how native apps have features that you just can't get in the browser. But what about the web-only features that you can't get natively? Possibly the most important feature like this is SEO. For many websites indexing is critical to success.
SEO is a very unexplored, and difficult thing to do with React Native (minus this article I wrote about using Expo with Next.js).

The Solution

Considering the issues I just laid out, the solution is somewhat obvious. React developers don't need "React Native", they need "React DOM rendered natively".
So I created a library which helps you do just that, called @expo/html-elements.
A set of (currently 40 new) light-weight, universal components named after HTML elements that help ease you into the React Native world without actually adding any overhead to your native project.

@expo/html-elements also help you accomplish:

  • An easier path for all users of React Native to implement common web functionality in their universal apps.
  • Optimized for SEO by using the correct DOM element whenever possible.
  • More automation around A11Y in your iOS, Android, and web projects.

Alt Text

Now if you want to build a simple link you can!

import { A } from '@expo/html-elements';

return <A href="#" target="_blank" />
Enter fullscreen mode Exit fullscreen mode

This link then converts to the following A11Y compliant link element while stripping away unused props:

Platform Output
Web <a dir="auto" href="#" role="link" target="_blank" />
Native <Text accessibilityRole="link" onPress={() => Linking.openURL('#')} />

Having an <a> element is good for a few reasons. You get the "copy link address" feature, the hover preview, peek and pop on iOS, and a few other things users have come to expect from the web.

Smarter Layouts

Using headers and other layout elements won't impact your native app, but not using them can impact your web search results. Consider the following page in my app:

Try it

import { View, Text, Button } from 'react-native';

export default () => (
  <>
    <Text>My Story</Text>
    <View>
      <Text>I did a thing with Lego now I code</Text>
    </View>
    <View>
      <Button title="follow me" />
    </View>
  </>
)
Enter fullscreen mode Exit fullscreen mode

Web crawlers and screen readers see a bunch of raw data like this:

<div>My Story</div>
<div>
  <div>I did a thing with Lego now I code</div>
</div>
<div>
  <div role="button" />
</div>
Enter fullscreen mode Exit fullscreen mode

If you were making a basic website with HTML (and not creating an app) you would probably use a variety of elements to ensure screen readers and crawlers work optimally:

<h1>My Story</h1>
<main role="main">
  <p>I did a thing with Lego now I code</p>
</main>
<footer>
  <div role="button" />
</footer>
Enter fullscreen mode Exit fullscreen mode

This tells the crawlers so much about our page, but how do we get this without compromising our native app? Well, to be honest it was actually pretty difficult and required a deep understanding of React Native web to figure out... But now with @expo/html-elements (!!) you simply:

import { H1, Main, P, Footer } from '@expo/html-elements';
import { Button } from 'react-native';

export default () => (
  <>
    <H1>My Story</H1>
    <Main>
      <P>I did a thing with Lego now I code</P>
    </Main>
    <Footer>
      <Button title="follow me" />
    </Footer>
  </>
)
Enter fullscreen mode Exit fullscreen mode

Now my page has universal A11Y features, and uses more of the correct DOM elements in the browser! 😎

Platform Output
Web <h1>My Story</h1><main role="main"><div>I did a thing with Lego now I code</div></main><footer><div role="button" /></footer>
Native <Text>My Story</Text><View><Text>I did a thing with Lego now I code</Text></View><View><Button title="follow me" /></View>

Due to the way text style inheritance works we still use a div for things like p, b, strong, etc. unless that text is a child, then it uses a span.

Getting Started

You can get started right away using snack: https://snack.expo.io/@bacon/blank-elements

- Or -

Create a universal project and get started using it locally:

  • Install the CLI npm i -g expo-cli
  • Create a new project expo init my-project
  • Install the package yarn add @expo/html-elements
  • Start the project with expo start

    • Press w to open in the browser
    • Press i to open iOS in the simulator
    • Press a to start the project on an Android emulator
  • Optionally: You can also use this package with any React Native tool

    • Ignite CLI: ignite-cli
    • Community CLI: @react-native-community/cli

Final Thoughts

Perhaps you haven't encountered any of the issues @expo/html-elements solves, or you think they could be solved in a different way, I'd love to hear your feedback.

I imagine some people may see this package and think that their native app is simply running in a web view like Cordova. This is absolutely NOT the case. Your views are still all optimally rendered as native views. If you encounter any misconceptions regarding this, I would appreciate you directing those folks to this post!

👋 Thanks for Reading

I hope this makes your transition from web development to web + native development even easier! If you enjoyed, staring the repo would be much appreciated: @expo/html-elements!

Keep in Touch!!

Top comments (7)

Collapse
 
technoplato profile image
Michael Lustig - halfjew22@gmail.com

A) I just started using expo and I can't believe how cool it is. Thank you for whatever you do with it. I don't know if you're the founder or just biggest evangelist.

B) How did you do the thing in your post footer with all of your cool buttons?

Collapse
 
evanbacon profile image
Evan Bacon

It’s an HTML table

Collapse
 
andrioid profile image
Andri

I've been preaching React Primitives (Text, View, etc) for so long that I'm a bit sceptical of reverting back to HTML lingo. I can see the value for web developers starting with RN though.

Collapse
 
evanbacon profile image
Evan Bacon

Ditto

Collapse
 
seanmclem profile image
Seanmclem • Edited

Just listened to you on react podcast, talking about how intuitive it was to use things like the text component to generate things like links in expo web. So

This might make it easier for a web dev like me to transition to RN. Thanks, I love to have options.

Collapse
 
evanbacon profile image
Evan Bacon

HTML-elements is a flavor of React Native that caters towards web developers. As a native developer I personally find the View, Text, Image, etc. primitives to be more intuitive.

Collapse
 
lfkwtz profile image
Michael Lefkowitz • Edited

Cool stuff, Evan! Shared on React Native Now #60