The easiest way (by far!) to build a real React + Firebase web app

Written by reactstudio | Published 2018/04/16
Tech Story Tags: javascript | react | firebase | cloud-firestore | web-development

TLDRvia the TL;DR App

Both React and Firebase are technologies that have been around for a while. 2018 has seen some exciting new developments that bring the two together in a way that’s greater than the sum of the individual parts — as awesome as they already are!

This article will show you how to build a realtime, highly scalable web app combining the two. Here’s a live demo of the result, a minimal “local Yelp clone” that lets you write reviews using your Google login:Restaurants Guide

You won’t have to write a single line of code to get to that point, yet the React (JavaScript/JSX) code will be there for you to modify and expand.

Before diving into the details, let’s recap what we’re talking about:

React is a JavaScript front-end technology created by Facebook. You could think of React as providing a crucial piece that’s missing from the web because HTML describes documents, not application UIs. React is a simple way to define the structure of a UI and how it’s updated over time as data changes. Originally developed for web apps, the React paradigm was so successful that it has since been applied to numerous other targets: there’s React Native for mobile apps, ReactVR for virtual reality, and so on. For this discussion, we’re focusing on React for web.

Firebase is a collection of services by Google for creating the back-end of mobile and web apps. Originally an independent startup, Firebase was acquired by Google and has since been integrated with Google’s global cloud infrastructure. The centerpiece of Firebase is a database, but it also provides all the other services you need for an app: there’s authentication, file storage, and cloud functions for running code on the server.

The original Firebase product was called Realtime Database. After the Google acquisition, Firebase has been creating a next-generation database product, and it’s now available as Cloud Firestore. (As of April 2018 the service is officially still in beta, but already works very reliably.)

This article is all about the new hotness of Cloud Firestore, not the “old” Firebase database. This is important because it’s rather easy to get confused about the products even on the Firebase website.

The tagline for Cloud Firestore is “Store and sync app data at global scale”. It’s fast, it’s realtime, it’s backed by Google’s auto-scaling infrastructure, and it has a nice admin UI that makes it easy to manage your data without having to memorize magic SQL incantations. It’s a true next-generation database for apps.

The only question mark is pricing. Firebase has good free and $25/month tiers, but beyond that, it’s metered: you pay by the amount of reads and writes made by users. What happens if your service gets very popular? Many people would argue that’s a nice problem to have — but regardless of that, the scalability of Google’s platform means that you won’t have to suddenly re-architect your entire back-end if usage explodes. That’s a cost reduction and risk management factor that you should take into account too. (If you’re still worried, read through this article: at the end we have some additional ideas for how to manage the Firebase pricing risk.)

React Studio ❤ Cloud Firestore

In short, there’s a lot to like about Cloud Firestore. But developing apps for an entirely new database presents a substantial learning curve. If you’re also trying to learn modern React practices at the same time, it can be quite daunting.

Enter React Studio. Created by Neonto, it’s a free application that bridges the worlds of UI design and development. You can bring in live data, import designs from tools like Sketch using our transfer plugins, add responsiveness and interactions, and build complete app flows. One click and you’ll get clean code in a fully functional React app structure (using Facebook’s Create-React-App scaffold).

That’s what makes React Studio a great learning tool for React programming. You can configure a fully working app, do changes in the GUI, and see how it affects the output code. When you want to go deeper, nothing stops you from jumping into a text editor and going crazy with the exported code. Thanks to integrated Git branch support, React Studio can be used to create and manage the UI even when you’re doing manual work on code in the same source tree. (We’ll have an article about that workflow soon!)

Get React Studio

You can get React Studio for free at: https://reactstudio.com

Currently it’s available only for Mac. (If you need a Windows version, it’s possible as part of a customized enterprise edition — contact sales@neonto.com. We’re super happy to do customizations, but please note that we can’t do custom development for Windows unless you’re committing to at least 10 seats on a long-term maintenance contract.)

Get the Firebase plugin

React Studio has an advanced plugin system that lets you switch between back-ends as easily as loading another plugin in the GUI.

In the latest React Studio 1.5 release, we’ve added a plugin to connect to Firebase Cloud Firestore. You can find it in the Plugin Store — click on the button in the toolbar. Even though the plugin has a nominal price in store credits, you get free credits after installing React Studio, so the Firebase plugin won’t cost you any money.

When the plugin is installed from the Store, you’ll be asked whether you’d want to open an example project. Choose that option because we’ll be using the example project through this tutorial. (If you accidentally miss it, don’t worry: the example project was saved in your Documents folder.)

Planning our journey: What’s in the Restaurants demo app

As mentioned at the start, what we’re going to build is a “local Yelp clone” like this:Restaurants Guide

When you downloaded the Firebase plugin from React Studio Plugin Store, you got an example project which is exactly the same as this Restaurants Guide demo. (If you don’t have the plugin yet, see steps above.) You should have the project open at this point. Note that it’s missing the Firebase setup — you’ll need to provide your own database information to make the app work. We’re going to do that as the first step shortly.

Here’s a summary of user-visible features in our app:

  • Login screen. Authentication (sign in) using Google login. You only need to be signed in to write a review, so there’s also the option to proceed without signing in.
  • Main screen. List of restaurants. The list is responsive (switches to fewer columns on a narrow mobile screen). Each restaurant item contains a “Details” button that takes you to its own screen.
  • Restaurant Details screen. Shows extended details for the restaurants and a list of reviews about the place. In the bottom-right-hand corner, there’s a floating action button with a + sign that lets you add a review.
  • Write Review screen. A simple form with the following data: your user picture (pulled from your Google login), your name and email (also from the login), title, rating using stars from one to five, and the review text itself.

Those are the core user-visible features for our “local Yelp clone”. However there’s a couple more things that are features we need to take into account when designing. It’s helpful to take a look at the Project Map in React Studio, as it reveals all the pieces that actually contribute to the UX design here:

We see a few things that were not mentioned in the above list of user-visible features:

  • The are actually two “Login Gate” blocks: one at the start and another one right before the Write Review screen. This one checks that the user is signed in before letting the user go any further. If the sign-in was already available, the Login Gate is automatically passed through to the next screen, so we might not have seen it.
  • On the right-hand side, there are two top-level components for a star shape, empty and filled versions. These are used in the Write Review screen. We’re using the Rating plugin there, and it has the option to take its visual appearance from external components. Having these on the top level gives us a useful view of the custom UI pieces we have in the project.
  • The main screen uses a component named UserInfo. This provides the login info at the top-right corner of the screen.

Setting up the database

Let’s jump in to Firebase. You can create a free account for Cloud Firestore at the product site. When you have an account set up, go into the Firebase Console and create a project for your Restaurants demo app.

Select your new project and go to Database. Make sure that “Cloud Firestore (Beta)” is selected using the button at the top (next to the big white “Database” header).

In Cloud Firestore, all data is stored in “documents” which are like the table rows in a traditional database. Each document must belong to a collection, which in turn is much like a table in a traditional database.

Click on “Add Collection” and make one called “restaurants”:

Click on “Next”. Now you’re asked to define the first document for this new collection. This step is important because it determines the type of data that your app will be storing in this collection. (It’s roughly equivalent to creating the table schema in a traditional SQL database, if that world is familiar to you. The difference is that Firebase collections don’t enforce the schema: each document can also contain other fields in addition to the ones you provide here for the first one.)

To describe a restaurant, we need a number of fields:

  • restaurantAddress
  • restaurantCoverImage
  • restaurantDescription
  • restaurantEmail
  • restaurantName
  • restaurantOpeningHours
  • restaurantPhone
  • restaurantWWW

For simplicity, use the “string” type for all. See screenshot above for an example of how you’d fill out the initial document with these fields.

(You don’t have to use the same names — for example, you might prefer to drop the “restaurant” prefix — but if you change the field names, you’ll need to update the React Studio document to use matching property names anywhere the data is linked. Also, before you do that, see below for an explanation of why the prefix is there.)

Once you have the first restaurant set up, do the same thing for reviews. Create a new collection named “reviews”:

…And fill out a first document with these fields, again with the “string” type:

  • restaurantID
  • reviewDate
  • reviewRating
  • reviewText

In the case of restaurants, it seemed redundant to add the “restaurant” prefix to all the fields. But here with reviews we see an advantage to this naming style, because one of the fields is named “restaurantID” rather than “review[something]”. This lets us easily glance at the fields and notice that there’s an external reference to another collection.

Our data model for the “local Yelp clone” is really simple. The two collections are entirely obvious except for that one “restaurantID” reference that ties reviews back to restaurants.

Collections and documents both get automatically generated object ids — long strings with numbers and letters like x3LBcxO4JwXc2. These object ids are unique and can be used to locate any piece of data within your Cloud Firestore database.

In order to make a review that always “belongs” to a particular restaurant, all we needed to do was create that “restaurantID” field. Our client app (the one made in React Studio) will be responsible for making sure that any new reviews that users create will contain a valid document id in the “restaurantID” field. If there are somehow reviews in the collection that don’t have a value for this field, it doesn’t break anything — those reviews simply won’t show up anywhere because the client app always requests reviews as filtered by a specific restaurantID using a query. We’ll see later how that works on the React Studio side.

Setting up database access rights

By default your collections aren’t visible to the outside world. For the Restaurants app, we want users to be able to both view the data as well as add new items through the client app.

Still under Database, go to the “Rules” page:

Enter the following security rule, as shown above:

service cloud.firestore {match /databases/{database}/documents {match /{document=**} {allow read, write;}}}

The rules language is fairly easy to understand. We’re telling it to match all documents anywhere in this database and allow both reading and writing. This lets you get started quickly, but you shouldn’t leave it like this permanently. Instead of opening up everything for reads and writes, you should give access only as needed. For example, only the ‘reviews’ collection actually needs write access since we’re not allowing users to add restaurants in the initial version of the demo app.

Read the Firebase security rules guide and make sure you understand what the client app can read and write. Securing your database is a crucial part of designing an app.

Setting up Firebase authentication

For the login to work, we need to enable Firebase authentication. Go into the Authentication tab and select the “Sign-in Method” page:

Enable only Google login for now. (You could turn on the others, but they have their various quirks: some like Twitter and Facebook need API keys from the service providers, while others like email and phone don’t provide all the data we’re using in the demo like user avatar pictures. To make things simpler for now, we’ll just go with Google first.)

Connecting Firebase to the React Studio project

Now we’re done configuring Firebase, and can jump into React Studio.

The first thing we’ll need to is to link the React Studio project to your new Cloud Firestore database. Click on the gear icon next to “Project Overview” to open the project settings. Then click on “Add Firebase to your web app” as shown below:

Firebase will show you a piece of HTML+JavaScript code. It contains the configuration we need to copy into React Studio:

React Studio doesn’t need this whole HTML enchilada of <script> tags though. All you need to copy is the stuff between curvy brackets after “var config =” (but do include the curvy brackets).

In React Studio, open the Data tab, then click on “Data Plugin Setup…”

On the left you’ll see a list of all data plugins in this project. Although the list shows all possible types such as Google Sheets and Generic JSON, they’re empty — the only item is under Firebase (Cloud Firestore) and it’s named “firebase-restaurantguide”. Click on that. Then click on the text area below “Firebase config (JSON)” and paste your Firebase config there, like this:

Click on “Close” to dismiss the data plugin setup sheet.

Then click on the Restaurants tab to view that data sheet, and click the “Reload data from service” button. You should see your Firebase restaurant data appear in React Studio. (If you get an error message about an index, don’t worry, it’s easy to fix — see below in the section How to add an index to your Firebase collection.) Do the same for Reviews: open the sheet and click “Reload data from service”.

That’s it! Your database is connected and we’ve verified the connection by loading data inside React Studio. You’re now ready to try how the client app feels.

Click “Open in Web Browser” in the React Studio toolbar. What this does is first export a copy of the client app as a React project, then run a local web server (at the URL localhost:3000) and open that site in your default web browser. After the export completes, you should see the browser open with a copy of the Restaurants guide app, but displaying the data you entered previously in Firebase Console.

Everything should work out of the box. You can log in with any number of Google accounts and post reviews under the single restaurant that you have configured in the database. (As users sign in, their information gets added to the Users list in Firebase Console > Authentication. That’s a convenient way to see who’s using your app.)

How Firebase collections are linked to React Studio data sheets

There are two important concepts that link Cloud Firestore to React Studio. The first is data sheets which are like “views” into your Firebase collections. The second is React’s properties which will be linked to fields in your Firebase documents.

Let’s first take a look at how data sheets are configured. You can create any number of data sheets in React Studio, but typically they would closely match your Cloud Firestore collections. In the Data tab, click on “Restaurants”. This displays a data sheet named, well, Restaurants:

The data sheet’s contents are loaded from Firebase, but it’s not automatic — you have to trigger it manually. (This is so that you can have consistency while designing.) Click “Reload data from service”, and you should see the data from your Firebase restaurants collection appear.

In the bottom-right corner are some interesting settings. Collection id is the name of the collection that’s associated with this data sheet. Query is a free field where you can enter queries written in the Cloud Firestore query format.

These queries are actually JavaScript method calls using the Cloud Firestore API. You can chain these methods with a dot, as shown in Cloud Firestore’s examples (see link above). For the Restaurants sheet, we have two query methods chained together: first orderBy(), then limit(). The orderBy() call simply ensures that data is returned alphabetically ordered by restaurant name. The limit() call has something more special going on, however. Its value is:

limit($slot('ds_numberOfRestaurants'))

$slot is a special template form available for queries in React Studio. It lets you look up a data slot’s value at runtime. Data slots are top-level variables that you can define and modify within React Studio (you can find them in the Data tab under “Slots”).

In this case, the value for the limit() query is being read from a data slot named ds_numberOfRestaurants. What’s going on is that we want to allow the user to choose how many restaurants are listed in the main screen. If you look at the demo app, you can see this option in the top-right corner. So, when the user selects a value in the UI, it gets saved in a data slot. Because the Restaurants data sheet has a query that refers to the data slot, it will get reloaded whenever the data slot value updates. All the machinery to watch data slots and reload the data from Firebase is automatically generated for you — all you need to do is use the $slot syntax in a query, and React Studio takes care of the rest.

Look at the Reviews data sheet next. There’s a slightly more complex query there:

Again we see an orderBy() method call, but before that there’s a where() method that looks like this:

where("restaurantID", "==", "$slot('ds_selectedRestaurantId')")

The where() query method is crucial because it lets us filter what information we want from a collection. In this case we only want reviews where the restaurantId field’s value is equal to the value of a ds_selectedRestaurantId data slot in React Studio. As discussed above, the use of the $slot syntax in a query guarantees that the data sheet will be refreshed whenever the data slot’s value is updated. In practice, what happens is that when the user clicks on a “details” button for a particular restaurant, that restaurant’s id gets saved in the data slot, which then makes the data sheet update.

Pretty simple, right? Let’s see how it’s actually done. But first a brief detour to fix a missing index, a common situation when working with Cloud Firestore queries.

How to add an index to your Firebase collection

When you add query methods to a data sheet, you may see an error message like the one shown here. This indicates that Cloud Firestore needs to have a composite index before the query can work.

Fortunately the Firebase developers have made it easy to fix the issue. The error message contains a link that points to the Firebase Console.

You can just copy the link from React Studio’s error message, paste it into a web browser, and you’ll be taken to a screen that shows the necessary index:

Just click on “Create Index”, then go back to React Studio and reload the data into the data sheet.

Properties and interactions for the Restaurant Item component

Go to the Project Map tab. In the middle there’s a component named “Restaurant Item”. Double-click it to open it for editing.

This UI component is what gets repeated in the list of restaurants. The topic is too large to cover here, but to learn more about how data, lists and properties are related, check out this detailed guide:Creating lists and using live data in React Studio

In the top-left corner of the canvas, you can see a list of properties like restaurantName, restaurantAddress, etc. From these names, thin blue lines point to UI elements that are linked to each property. For data linkage to work correctly, the used property names must match the document fields in your Firebase restaurants collection earlier. You can click on any element to view it in the Inspector panel on the right, and then open the Data tab to view how properties are linked.

Let’s see how the interaction is done. Click on the “Details” button in the canvas. Choose the Interact tab and you’ll see the following:

There’s a bunch of stuff going on here. The Tap interaction actually has two actions attached to it — you can tell by the numbers 1/2 at the top next to “When user taps”. The first action is “Save data”, and its target is a data slot named ds_selectedRestaurantId. (Remember that’s the same data slot that was used in the query for the Reviews data sheet.)

The value that gets saved in the data slot is taken from a component property. This part is somewhat non-obvious and requires explanation. To get the restaurant’s id that pertains to the current item, we’re accessing a property named datasheetRow.key. How does this work?

The datasheetRow property is provided by React Studio whenever a component is used within a list. It gives access to the entire contents of the row within the data sheet. Because our datasheet is sourced from Firebase, that means the datasheetRow property will simply contain the Firebase document (for a restaurant in this case).

But what’s “key”? We didn’t have such a field in Firebase. This value is actually being provided by the Cloud Firestore plugin which puts the document’s id in there. The property name “key” is special in React: it’s required for data that’s used in lists so that React knows which items are unique when the data changes. The Firestore document id neatly fulfills this role, and thus what Firestore calls “object id” becomes “key” in React.

Long story short: when a component is used inside a list, you can always find the document’s id in the datasheetRow.key property.

Next click on the little “2” button to show the other action:

This action is of type “Go to” and links to the RestaurantDetails screen. Actions are chained, so when the user clicks on this button, first the value of datasheetRow.key gets saved in the data slot, then this “Go to” action gets executed.

This way, the RestaurantDetails screen is always entered in a state where the Reviews data sheet already contains only those reviews pertinent to a particular restaurant. (Remember that the query on the Reviews data sheet is the glue that makes this work.)

An interesting setting here is “Carry properties from parent component”. It’s set to the value “All”. This means that any properties from this list item will be passed on to the next screen.

We can open the RestaurantDetails screen to get an idea of why that’s useful:

In the top-left corner of the canvas, we again see properties that are being used. A bunch of them are marked “carried by nav”, for example restaurantCoverImage, restaurantName, etc. These properties were simply copied over from the list item that led here.

Note that the datasheetRow property implicitly gets included in the carry, so our details screen has full access to the Firebase document’s contents. That gives us access to any additional properties that might not have been used by the list item.

The Firebase Login element

We previously saw the Cloud Firestore data plugin when we pasted in the configuration. Actually that’s not the only plugin that got installed in React Studio when you downloaded the Firebase package from the React Studio Plugin Store. There’s another one called Firebase Login, and you can see it in action in the Login Gate blocks.

Double-click on the Login Gate that’s close to the Write Review screen. Then click on the “Sign up with Google” element, and you’ll see the settings for the Firebase Login plugin displayed:

There’s a bunch of settings here. You can choose which login buttons to show, whether to save user’s info into data slots, and lastly there’s a checkbox to enable automatic sign-in for returning users.

The Firebase Login element works in collusion with the Login Gate screen type. You can place this element in any Login Gate (drag it from the list of elements on the right of the canvas). The element will hook up its code generation with the Login Gate, so there’s a central place (independent of the plugin) where you can decide what happens when login is successful.

Click on the Screen tab:

For the Firebase login to work correctly, it’s important that you select “Web service” login type, and choose the right plugin under the “Service” button menu.

The end — or maybe the beginning of something great

We covered a fair bit of ground, but there’s a lot of stuff that couldn’t fit in while keeping this post reasonably long. Check out the following posts to learn more about React Studio:

To leave you with a parting thought… If you’re worried about Firebase pricing, consider what you just read about how the Firebase plugins work in React Studio. To recap: there’s a “Cloud Firestore” web service plugin that’s responsible for loading data into data sheets and sending form data to the database, and then there’s another “Firebase Login” plugin that was placed into a Login Gate to provide the authentication UI.

What if you wanted to replace Firebase with a different back-end? All you’d need to do is swap out those two plugins with something else. It could be either a custom back-end or some other “Backend-as-a-Service” type company. Whatever the back-end is, you can wrap it into a React Studio plugin because the plugins are actually just JavaScript code generation templates.

If you want to see for yourself, open the Plugin Manager from React Studio’s Plugins menu, then select the Cloud Firestore plugin and click “Edit”. You’ll see the structure of the plugin right there. It’s all written in JavaScript and located as a bundle in the file system. To swap a different back-end in its place, you could literally make a copy of the Cloud Firestore plugin and replace the codegen templates.

This is how React Studio gives you entirely new flexibility on the front-end. No longer are back-end specific choices irrevocably enshrined in your codebase, but instead they’re isolated in plugins. Want to get out of Firebase? Swap the plugins, click “Export”, and suddenly your front-end is connected to a different service. Doing that kind of deep modification manually to a codebase can be a lot of work and easily creates new bugs.

Update January 24th 2019

We’ve released five step Youtube video series for creating a Chat app with React Studio and Firebase Cloud Firestore backend. Find the full episode playlist from below.

https://www.youtube.com/watch?v=1OZFsgHa3fE&list=PLKrYy47a9BRleRGnLAarv5zkf_2eDUrh6

Thanks for reading, and see you soon in the amazing world of Firebase and React Studio!


Published by HackerNoon on 2018/04/16