👑⚙️ Smaller, Faster Websites with Preact and Expo
Tags: tutorial, javascript, react, webdev
Published: 2020-01-22 18:13:53

React Native for web is **awesome** because not only can you reuse all of the dope native Expo packages like [expo-camera](https://www.npmjs.com/package/expo-camera) and [react-native-gesture-handler](https://github.com/software-mansion/react-native-gesture-handler), but you can also use any of the neat things coming out of the React/web community to create blazingly fast websites.

A great example of this is the library Preact by [Jason Miller](https://twitter.com/_developit)! [Preact](https://preactjs.com/) is a light-weight (~3kb) alternative to React. 
When building a web-only project setup is simple, you can just replace React with Preact and start coding right away, but React Native for web is a little bit more complicated. 
Because RNWeb uses many different React features we have to really push Preact to its limits to get it working with Expo for web. Luckily it's still pretty simple to setup.

# Before Preact

Before we cover the how, let's talk about why you might want to use Preact. If you create a standard Expo web project (at the time of writing this article), the bundle size will look something like the report below. 

**❌ 60.75 KB Gzipped**

![expo web bundle without preact](https://thepracticaldev.s3.amazonaws.com/i/2wnj5q9p9cfdvz5wt0wj.png "Expo web bundle without Preact")

# After Preact

If you configure your project to use Preact instead of React, the bundle will reduce drastically! This means your website will load faster and work much better for users with poor internet connections or low-end devices.

**✅ 27.98 KB Gzipped**

![expo web bundle with preact](https://thepracticaldev.s3.amazonaws.com/i/xn5j13vhxfaohz462uer.png "Expo web bundle with Preact")

# ⚽️ Getting Started

To use Preact with React Native for web, you'll need to install the packages and alias them to React.

> 💡 The most updated info is in the Expo docs: [Using Preact](https://github.com/expo/expo/blob/master/docs/pages/versions/unversioned/guides/using-preact.md)

- Install Preact (requires Preact 10+): 
  - `yarn add preact-responder-event-plugin preact`
  - or `npm install --save preact-responder-event-plugin preact`
- Create a custom Webpack config for the Expo CLI to use:
  - Run `expo customize:web` 
  - Select `webpack.config.js`
- Modify the Webpack config to use Preact instead of React:

  ```js
  const createExpoWebpackConfigAsync = require('@expo/webpack-config');
  const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

  module.exports = async (env, argv) => {
    const config = await createExpoWebpackConfigAsync(env, argv);

    // Add more aliases
    config.resolve.alias = {
      ...config.resolve.alias,
      // Use Preact aliases
      react$: 'preact/compat',
      'react-dom$': 'preact/compat',
      // Fix the responder system which react-native-web depends on
      'react-dom/unstable-native-dependencies$': 'preact-responder-event-plugin',
    };

    // Optionally you can enable the bundle size report. 
    // It's best to do this only with production builds.
    if (env.mode === 'production') {
      config.plugins.push(new BundleAnalyzerPlugin({
        path: 'web-report',
      }));
    }
    return config;
  };
  ```

- Running `expo build:web` will now use Preact instead of React. This means you'll now have a significantly smaller bundle! 🚀

# Drawbacks

To achieve a smaller bundle size Preact has to cut a few features. For instance they don't support synthetic events. Another major drawback is that usage with React Native isn't very clear, if you're building a universal app you may want to consider using the same version of React across web and mobile. For more information on issues using Preact with React Native for web, please refer to this issue [react-native-web#1219](https://github.com/necolas/react-native-web/issues/1219) (link courtesy of Max Thirouin)

# 👋 That's all

Thanks for reading, that's all I've got for you today. Let me know if you enjoyed this article, and reach out if you have any more questions!

<table>
  <tr>
 <td><a href="https://twitter.com/baconbrix">
    <img alt="Twitter: baconbrix" src="https://img.shields.io/twitter/follow/baconbrix.svg?style=flat-square&logo=TWITTER&logoColor=FFFFFF&labelColor=00aced&logoWidth=20&color=lightgray" target="_blank" />
  </a>
</td>
<td><a href="https://github.com/evanbacon" aria-label="Follow EvanBacon on Github">
    <img alt="Github: evanbacon" src="https://img.shields.io/github/followers/evanbacon.svg?label=Follow&style=flat-square&logo=github&logoColor=FFFFFF&labelColor=24292e&logoWidth=20&color=lightgray" target="_blank" />
  </a></a>
</td>
<td><a href="https://www.instagram.com/baconbrix" aria-label="Follow Baconbrix on Instagram">
    <img alt="Instagram: baconbrix" src="https://img.shields.io/badge/@baconbrix-E4405F.svg?style=flat-square&logo=INSTAGRAM&labelColor=000&logoWidth=20" target="_blank" />
  </a>
</td>
<td><a href="https://www.linkedin.com/in/evanbacon" aria-label="Connect with Evan Bacon on LinkedIn">
    <img alt="LinkedIn: evanbacon" src="https://img.shields.io/badge/connect-0077b5.svg?style=flat-square&logo=LINKEDIN&labelColor=000&logoWidth=20" target="_blank" />
  </a>
</td>
<td><a href="https://www.youtube.com/c/exposition?sub_confirmation=1" aria-label="Watch Evan Bacon on YouTube">
    <img alt="YouTube: Evan Bacon" src="https://img.shields.io/badge/Watch-FF0000.svg?style=flat-square&logo=YOUTUBE&labelColor=FF0000&logoWidth=20" target="_blank" />
  </a>
</td>
<td><a href="http://evanbacon.dev" aria-label="Learn more about Evan Bacon">
    <img alt="Website: Evan Bacon" src="https://img.shields.io/badge/Portfolio-4630EB.svg?style=flat-square&logo=EXPO&labelColor=000&logoWidth=20" target="_blank" />
  </a>
</td>
</table>