DEV Community

Cover image for 🚀 Gatsby + React Native for Web + Expo 🥳
Evan Bacon
Evan Bacon

Posted on • Updated on

🚀 Gatsby + React Native for Web + Expo 🥳

TL;DR: 🎳 Example 📚 Docs

Alt Text

In this post I'll show you how to setup your (universal) Expo app to use Gatsby when targeting web!

🚨 If you find bugs please report them on the expo-cli repo with the [gatsby] tag in the title.

Why use Gatsby with Expo?

Here are the main benefits of doing this:

  • You can prerender the web part of your Expo app.
  • If you use Gatsby already, then now you can share most of your code between web and mobile.
  • If you're new to Expo then this also means that you can use complex browser features from the Expo SDK like Camera, Gestures, Permissions, etc. whenever possible in the browser!

⚽️ Tutorial

Alt Text

  • Install or update the Expo CLI: npm i -g expo-cli
  • Create a new Expo project: expo init
  • Install the Gatsby plugin:
  # yarn
  yarn add gatsby gatsby-plugin-react-native-web

  # npm
  npm install --save gatsby gatsby-plugin-react-native-web
Enter fullscreen mode Exit fullscreen mode
   module.exports = {
     plugins: [
       /* ... */
Enter fullscreen mode Exit fullscreen mode
  • Add /.cache and /public to your .gitignore
  • Now create the first page of your Gatsby project:
    • Create the pages folder: mkdir -p src/pages
    • Create the file:
  # JS
  cp App.js src/pages/index.js

  # TS
  cp App.tsx src/pages/index.tsx
Enter fullscreen mode Exit fullscreen mode
  • Run yarn gatsby develop to try it out!
    • Open the project in the browser http://localhost:8000/

Alt Text

Trouble Shooting

If you started your project without the Expo CLI you may also need to do these things:

  • If you started your project with Gatsby CLI then check out the guide I wrote in the docs: Gatsby Project with Expo.

  • Install React Native for Web: yarn add react-native-web

  • Install the babel preset for React Native web:

  # yarn
  yarn add --dev babel-preset-expo
  # npm
  npm install --save-dev babel-preset-expo
Enter fullscreen mode Exit fullscreen mode
  • 💡 Even though the name has Expo in it, you can (and should) use it for any React Native project as it provides universal platform support.
   module.exports = { presets: ['babel-preset-expo'] }
Enter fullscreen mode Exit fullscreen mode

🏁 New Commands

  • Starting web

    • 🚫 expo start:web
    • ✅ yarn gatsby develop
  • Building web

    • 🚫 expo build:web
    • ✅ yarn gatsby build
  • Serving your static project

    • 🚫 serve web-build
    • ✅ yarn gatsby serve

📁 File Structure

Here is the recommended file structure for a Expo project with Gatsby support.

Expo Gatsby
├── src
│   └── pages ➡️ Routes
│       └── index.tsx ➡️ Entry Point for Gatsby
├── .cache ➡️ Generated Gatsby files (should be ignored)
├── public ➡️ Generated Gatsby files (should be ignored)
├── assets ➡️ All static assets for your project
├── App.tsx ➡️ Entry Point for Mobile apps
├── app.json ➡️ Expo config file
├── gatsby-config.js ➡️ Gatsby config file
└── babel.config.js ➡️ Babel config (should be using `babel-preset-expo`)
Enter fullscreen mode Exit fullscreen mode

👋 Thanks for Reading

I hope you found this article helpful! If you do use this workflow I'd love to hear about how it worked for you 😁 If you didn't, I'd also love to hear! So go tweet some emojis at me :]

📚 More Info

Top comments (10)

jstansbe profile image
jstansbe • Edited

When adding gatsby and react-native-web to an existing expo project with the above instructions, yarn gatsby develop will error out. Something about gatsby graphql calls at compile time.

Install babel-plugin-remove-graphql-queries plugin and update the babel config file.
yarn add --dev babel-plugin-remove-graphql-queries

module.exports = function(api) {
  return {
    presets: ['babel-preset-expo'],
+    plugins: [['babel-plugin-remove-graphql-queries', {}, 'test-name']],

I found the fix here:

sebastienlorber profile image
Sebastien Lorber

It's been fixed in 3.0.0-beta.7
Please let me know if you have other issues or if you build something meaningful with the plugin.

You shouldn't need a local babel config anymore (but it can be useful if you are using the same folder as a gatsby app + an Expo app, like in this setup).

It may be worth to read also the plugin's readme, as I've just updated it.

motin profile image
Fredrik Wollsén • Edited

Thanks for this! This got me up and running with SSR in my Typescript Expo project, awesome! However, there is some conflict in Expo's and Gatsby's webpack definitions config which results in process.env.NODE_ENV being set to the string "development" in gatsby-ssr.js and in plugins when running gatsby production builds. Workaround:

exports.onCreateWebpackConfig = ({ getConfig, actions }) => {
  const existingConfig = getConfig();
  const { plugins } = existingConfig;

  // Remove conflicting expo definitions entry which sets process.env.NODE_ENV to "development" always
  if (process.env.NODE_ENV === "production") {
    const filteredPlugins = plugins.filter(plugin => {
      return !(plugin.definitions && plugin.definitions.__DEV__);
      plugins: filteredPlugins
sebastienlorber profile image
Sebastien Lorber


I'm maintaining the plugin, can you open an issue about this problem?
I'm not totally able to understand, how is it causing troubles? Does it use react development mode even in production build?

keung7252 profile image
keung7252 • Edited

Duplicate "graphql" modules cannot be used at the same time since different
versions may have different capabilities and behavior. The data from one
version used in the function from another could produce confusing and
spurious results.
at instanceOf (/Users/xxx/projects/expo_boilerplate/node_modules/gatsby-recipes/node_modules/graphql/jsutils/instanceOf.js:28:13)
at isInputObjectType (/Users/xxx/projects/expo_boilerplate/node_modules/gatsby-recipes/node_modules/graphql/type/definition.js:168:34)
at typeMapReducer (/Users/xxx/projects/expo_boilerplate/node_modules/gatsby-recipes/node_modules/graphql/type/schema.js:290:41)
at Array.reduce ()
at typeMapReducer (/Users/xxx/projects/expo_boilerplate/node_modules/gatsby-

at Array.reduce ()
at new GraphQLSchema (/Users/xxx/projects/expo_boilerplate/node_modules/gatsby-recipes/node_modules/graphql/type/schema.js:145:28)
at Object. (/Users/keung/projects/expo_boilerplate/node_modules/gatsby-recipes/dist/graphql-server/server.js:25614:18)
at Module._compile (internal/modules/cjs/loader.js:1137:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)

hot update was not successful
hot update failed for module "./.cache/app.js". Last file processed: "./.cache/root.js".

"gatsby-plugin-react-native-web": "^3.1.0"
"gatsby": "2.0.0",

it only works on 2.0.0, not 3.0.0

andrioid profile image

Amazing stuff Evan! I can't wait to try this out.

evanbacon profile image
Evan Bacon

Great stuff as always @evanbacon

rohovdmytro profile image
Rohov Dmytro


Thank you.

yeich profile image
Yannick Eich • Edited

I didint got it. why did You choose Expo?

andrioid profile image

He works on Expo.

Other reasons why this is cool:

  1. Create an app for iOS and Android, then reduce the installation barrier by offering a PWA for very little extra work
  2. Using Gatsby for something like landing pages with reusable logic from your apps <3

I've used Expo in production for years and it's rock solid. There are a lot of people that think Expo is somehow inferior to a vanilla React Native project. It's mostly the same stuff, and unlike many other frameworks, Expo makes it pretty easy to "eject" if you need it.