Using Expo, Gulp, and Webpack to Create and Publish React Components to NPM

Combining Tooling with Automation to Build Something that Contributes to the Community

Reginald Johnson
React Native Training
9 min readFeb 15, 2018

--

If you’ve read my previous posts, you’ve probably gathered that I’m working on some sort of side-project. Hopefully, I will get to the point that I’m ready to release my creation, but for now I’ve been content to adjust and tweak as needed. For example, I’ve repeatedly gotten to the point where it made logical sense to break out significant pieces of functionality and place them in their own NPM modules.

Doing this provides multiple benefits. It allows other people to use, and help improve the functionality the package provides. It also provides the benefit of improved reuse in another project (there’s always another project) by just doing an npm install (or yarn add if you prefer). Additionally, it would allow me to “eat the elephant one bite at a time” vice being confronted with my entire project in one go. I still have a day job, so any ability to modularize my task helps contribute to its accomplishment.

It was with all this in mind that I created the react-native-webview-quilljs and react-native-webview-leaflet packages. In this post, I will go over the technique I used to build, package, and publish those components using expo.io, webpack, and gulp. Specifically, I will cover how I built an application using Expo, used Webpack to package the files required by a React Native WebView component, and then used gulp to automate a build and publishing process.

Gulp is going to take us there and bring us back

Design

This post assumes some familiarity with Expo, Webpack, Gulp and NPM so I’m only going to discuss the process required to go from an Expo project to published NPM package. This is a multiple step process:

  1. Use Expo to create an application containing the functionality you wish to publish
  2. Separate that functionality into a separate component within the project
  3. Use a Gulp task to automate the packaging and NPM publishing process.

Creating an application with Expo

I previously discussed the use of react-native webviews to incorporate plain JavaScript functionality into a react-native application in my four part series “Accepting Payments in a React Native App”. In particular, part four: Building the Payment Client Using React Native and React shows that using a Webpack pipeline to compile React results in a project that can be imported into a React-Native webview. Basically, as long as your pipeline delivers an html file that the webview can use as its source, you should have a working Expo project that can be run via the Expo XDE.

That project used Webpack to create a “dist” folder containing an HTML file and the associated JavaScript and CSS files needed to display the React Component that encapsulated the JavaScript library that I was attempting to run. For the above referenced project project, I created a WebView that used Braintree’s payment library.

However, an unanticipated and very ugly problem revealed itself when I finally attempted to run the project after publishing it to Expo; the HTML file (and it’s JavaScript and CSS files) required by the WebView was not packaged for publication. That’s because, at the time I am writing this article, Expo is only capable of bundling certain types of files with standalone applications. This limitation is discussed as part of the asset bundling guide in their offline support features. However, I would not be surprised if Expo got around to including offline support for HTML, CSS, and JavaScript files at some point. Once they do, then a significant portion of this post will be a “moo” point…

Friends, creating unrealistic housing expectations for millions

This was a problem because the project would not work when published. It only worked in the development environment on the local computer. And without publication there is no demo project, and without a demo project there is not NPM package, and with no NPM package there is no glory!

Or infamy, you know how the Internet is.

Predictably, this issue continued I published the package to NPM and used it in another project. The component in the NPM package itself contained a WebView that I had anticipated would be able to access the index.html file that was also located in the same NPM package. Unfortunately, that was not the case. The WebView rendered; however, it was not able to access the HTML file that was literally just a directory away.

Update (16 Feb 2018)

Expo SDK 25’s asset bundling provides support for including CSS, HTML, and JavaScript files in a published package. This means that the upload technique described below shouldn’t be necessary if you are using that version or newer. You can learn more about Expo asset bundling here. Now back to the original content.

Update (2 Mar 2018)

It looks like the Expo/React-Native ecosystem isn’t quite ready to bundle JavaScript files into published packages. My exploits trying to do this are documented in this thread on the Expo forums. I hope to find an alternative to the work-around described below, and will publish a new article when I do.

To summarize, a WebView with a source=require(‘./dist/index.html’) when “./dist/index.html” was in the same package wasn’t displaying anything. This problem is discussed more in depth in this GitHub issue.

In addition to the work-arounds listed in the issue tracker, Expo form user burak proposed the technique of uploading the necessary HTML, JS, and CSS files to the Internet and then download them to the mobile for use in the WebView. This is the technique I chose to follow.

An online repository or CDN will suffice for this as long as you have a URL that points to the file(s). Then, those files can be downloaded to the local device the first time the component containing the WebView is mounted, or if a version change is detected. This process is defined by the following steps:

It’d be nice if Medium allowed for formatting as an outline

At this point, the index.html file and its dependencies are available for the WebView to display.

This functionality is encapsulated in the versioned-file-downloader NPM package. The repo for that package can be found here. This package uses the Expo.FileSystem API to download files to the device’s document directory. It looks for and downloads files into a folder structure that looks like this:

Once the file(s) have been successfully downloaded, they can be accessed using the WebView’s “source” prop like so:

At this point we have a way to obtain and display the files required by the WebView and the current situation can be summarized as follows…

Yes, its an old meme, but it checks out

Separating the Package from the Expo Project

Before extracting the files for publication, there needs to be a clean division between what will be the NPM package and the Expo application itself. The goal is to separate anything that might be reusable and place it into a file that will be imported. The desired goal is to have a file that exports a function that can be imported by another file. This function is then called with some number of arguments. Upon completion, it may or may not return something back to whatever called it initially.

Basically, create a function, throw it in a file, export it, and you have the start of a package.

Extracting the Package

There are a few distinct steps that have to be performed in order to extract the necessary files from the Expo Project and publish the NPM package:

  1. Webpack must be run whenever the files HTML, CSS, or JavaScript files used by the Webview are changed in order to create the updated files for use by the WebView
  2. The version number in package.json must be increased prior to publishing the package
  3. A push to GitHub is required whenever we want to update the files that the WebView uses so that the WebView has access to the most up to date files
  4. Most insidiously, there are two changes that must be made to the “package.json” file and then reverted. Keep in mind that this file is used by Expo and NPM.
  5. First is the “main” parameter in package.json. Remember, the files that will become an NPM package sit inside an Expo project. Expo projects require main to be equal to “node_modules/expo/AppEntry.js” in order to run as an Expo project. However, that won’t work for an NPM package which must be a JavaScript file contained in the package, and typically is set to “index.js”. This means that the “main” value must change prior to NPM publishing and then change again prior to running the Expo application that encompasses the package.
  6. Next, the dependencies must be changed. Expo projects require an expo, react, and a special react-native dependency. Additionally, this WebView technique requires a react-dom dependency. However, none of those are requirements for the NPM package. In fact, I encountered breaking errors when I attempted to run the project with an NPM package that included those extraneous dependencies. This means that the dependency list must be different when publishing and running the Expo project

The bottom line is that the “main” and “dependencies” values in package.json must be one thing when publishing and something different when running the application. Since I still want to be able to run the application as an Expo project, I will have the change the values for main and dependencies to the appropriate values for publishing and then back to the correct values for an Expo project.

Automation with Gulp

Fortunately, these steps are predictable and repeatable, which means they are the perfect fit for a task runner like Gulp. A suitable gulpfile can be found at this repo. The primary tasks are “dev” and “prod” which can be run like any gulp task: “gulp dev” or “gulp prod”. The gulp dev task is show below that performs all those steps is shown below:

The process accomplishes the cycle shown in the picture below:

Running the gulp tasks results in a published NPM package suitable for use in other projects while still leaving a project suitable for running as an Expo project. Gulp is not the conductor of the publication workflow.

Conclusion

As previously stated, this article is not meant to be a full blown explanation of how to use all the tools it mentions. Each of the tools has significant amounts of documentation on their usage that I’d recommend familiarizing yourself with if you have questions.

Instead, this article is intended to describe a technique of combining multiple tools to achieve the desired goal; a published NPM package.

Expo provides the ability to quickly create and share mobile applications using React Native. React Native provides a WebView component that lets us display HTML, CSS, and JavaScript content inside our application. Webpack automates the build process and can create the necessary files for the WebView component to display. And finally, Gulp automates the processes that are required to have a working Expo project and a publishable NPM package.

Reginald Johnson is a 20+ year military veteran who is blogging, creating, and teaching his way through his transition into software development. You can follow him on Twitter at @reginald3, or connect with him on LinkedIn at https://www.linkedin.com/in/reggie3/

--

--

Reginald Johnson
React Native Training

Freelance software developer and 21 year military vet. I build for the web, mobile, and server with React, React Native, and AWS & node. https://www.reggie3.com