Navigate back to the homepage

Using Expo in Gatsby

Sébastien Lorber
May 11th, 2020 · 3 min read

You probably heard of React-Native-Web, allowing to render React-Native views on the web.

So, what about using React-Native in a Gatsby website?

This post is a proof-of-concept: it contains React-Native and Expo interactive components, embedded directly into this article (using MDX), of my open-source Gatsby site.

If you are not familiar with the React-Native ecosystem:

  • React-Native-Web: it is like a CSS-in-JS library (similar to Emotion or styled-components) that reuse the existing React-Native APIs. It has interesting properties (like atomic CSS-in-JS).
  • Expo: it is like an extension of React-Native: it provides a large SDK with much more APIs than core React-Native (camera, battery, video, audio, qrcode…), enabling you to build more complex experiences.

The idea is that I can use such code in my Gatsby site:

1import { TouchableOpacity, Text } from 'react-native';
3export const MyTestButton = () => (
4 <TouchableOpacity
5 onPress={() => alert('onPress')}
6 style={{
7 padding: 10,
8 backgroundColor: 'blue',
9 borderRadius: 5,
10 }}
11 >
12 <Text style={{ color: 'white' }}>Click me</Text>
13 </TouchableOpacity>

And it should work fine in my Gatsby pages, but also in MDX content:

1# Blog title
3blabla this is a MDX blog post using an embedded RN button:
5import { TouchableOpacity, Text } from 'react-native';
8 onPress={() => alert('press')}
9 style={{
10 padding: 10,
11 backgroundColor: 'blue',
12 borderRadius: 5,
13 }}
15 <Text style={{ color: 'white' }}>Click me</Text>

Let’s run this Hello world code and see if it works:

Click me

Why ???

  • blogging about React-Native
  • cross-platform: sharing code between your mobile app and a static website
  • using React-Native-Web, as it’s a performant atomic CSS-in-JS library

I have good hope to see more React-Native developers embed runnable code in their blogs, like I did in this post.

Demo time

Keep in mind that all the demos are written in a cross-platform way, using only React-Native and Expo apis.

All these demos can also run (natively) in iOS and Android (natively).

There’s no usage of a single div, or any direct browser or DOM api usage.

So, let’s start with a simple one. Can I render a complex svg with react-native-svg? Yes!

Can I use a cross-platform third-party component, like expo-dark-mode-switch, and wire it to my Gatsby theme-ui state? Yes!

Can I ask your permission to use your camera, and reveal it with a fade-in animation? Yes!

Show expo-camera

Can I use more complex gesture-based systems? Yes! Demo credits to Evan Bacon.

Can I play a video? Yes!

Show video

Can I build an image picker, and enable a few image transformations? Yes!

Pick an image

In case you wonder what the code looks like, here’s a snippet for the camera demo.

You can find the rest of the code here.

1import { View, Text } from 'react-native';
2import { Camera } from 'expo-camera';
3import * as Permissions from 'expo-permissions';
5import MobilePhoneView from 'components/MobilePhoneView';
6import AppButton from 'components/designSystem/AppButton';
7import AppRevealView from 'components/designSystem/AppRevealView';
9export const ExpoCameraDemo = () => {
10 const [showCamera, setShowCamera] = useState(false);
11 return (
12 <MobilePhoneView safeAreaPaddingTop={0}>
13 {showCamera ? (
14 <AppRevealView>
15 <Camera style={{ flex: 1, width: '100%' }} />
16 </AppRevealView>
17 ) : (
18 <View style={{ flex: 1, justifyContent: 'center' }}>
19 <AppButton
20 onPress={async () => {
21 const result = await Permissions.askAsync(
22 Permissions.CAMERA,
23 );
24 if (result.status === 'granted') {
25 setShowCamera(true);
26 }
27 }}
28 >
29 Show expo-camera
30 </AppButton>
31 </View>
32 )}
33 </MobilePhoneView>
34 );

gatsby-plugin-react-native-web v3

I created gatsby-plugin-react-native-web, and version 3.0 is now out of beta.

It uses under the hood @expo/webpack-config, which permit to get started fast without any config (thanks Evan Bacon).

Note: you can use React-Native and Expo with NextJS too. is build with NextJS and Expo. You can use @expo/webpack-config on any project using Webpack.

After adding the required dependencies, you only need to add the plugin to gatsby-config.js, and nothing else is required (0 plugin config to provide). This blog post does not need any fancy additional config to render properly. Expo APIs, and your favorite React-Native libs, should work out of the box.

The best way to get started is to use the new Gatsby Recipes feature:

1gatsby recipes

Otherwise you can do these steps manually

Step 1

Add required dependencies to package.json

1yarn add react-native react-native-web@~0.11.7 gatsby-plugin-react-native-web

If you want to use Expo APIs, and advanced animations, you can install these too:

1yarn add expo react-native-gesture-handler react-native-reanimated

Step 2

Add the plugin in gatsby-config.js:

1module.exports = {
2 plugins: [`gatsby-plugin-react-native-web`],

Step 3

Use React-Native and Expo components in your Gatsby site.

Check the React-Native-Web and Expo docs for available web platform support.

You can also browse libraries with web support on

Toward cross-platform content

All these demos use React Native and Expo code, and they can run natively on React-Native, because they only rely on React-Native primitives. These demos are embedded in this markdown post using MDX. Fortunately, MDX can be run on React-Native too!

This actually means that… my posts are cross-platform?

Stay tuned: in upcoming blog posts, I’ll show you how I run my cross-platform MDX blog posts inside an Expo app.


For a preview, scan my Expo QRCode, or take a look at the source code :)



We need more people to adopt cross-platform development, to make it mainstream and polished.

The setup to get started has never been so simple, it’s time to jump on the bandwagon, and get started with React-Native-Web.

Thank you for reading!

If you like it, spread the word with a Retweet

Browser code demos, or correct my post typos on the blog repo

For more content like this, subscribe to This Week In React and follow me on Twitter.

Join This Week In React - Stay up-to-date now!

A weekly newsletter with all the cutting-edge React and React-Native news. Perfect for experienced developers. Stop scrolling on Twitter and receive everything directly in your inbox!

    More articles from Sébastien Lorber

    Atomic CSS-in-JS

    ...or how to scale your CSS-in-JS.

    April 27th, 2020 · 7 min read

    Handling API request race conditions in React

    ...or how to avoid rendering the wrong data

    August 30th, 2019 · 6 min read
    © 2019–2020 Sébastien Lorber
    Link to $ to $ to $ to $ to $ to $mailto:[email protected]