In this tutorial, we’ll create a simple movie-themed web app using Airtable and React. So, what are you waiting for? Let’s get started!

Table of Contents

Introduction

Before we begin, I’m sure some of you are wondering what Airtable is.

Airtable looks and feels like a spreadsheet, but it’s actually a database with an easy to use API that we can plug straight into our front-end with little effort.

Just like in Google Sheets how you add your data in rows and columns, Airtable also lets you add data in rows and columns. The user interface feels very familiar to Google sheets, making it very easy to understand and use by non-technical people.

If you’d like to read more about Airtable before we get started on our web app, you can check out their excellent getting started guide.

What Are We Making?

We’re keeping with the movie theme in this tutorial because I’m a huge fan of movies . Therefore, by the end of this tutorial, you’ll have created a great looking web app built using React and Airtable that lets you rate your favorite movies!

I try to stick with the same tools and libraries in all of my web apps. Therefore, we’ll be using the following stack:

React

Bootstrap 4.1

Airtable

That’s it! Just three ingredients for a delicious web app! Let’s open our text editors and terminals and begin, shall we? Oh, if you’re using Atom, be sure to check out the Best Atom Plugins for Front End Developers.

Setting up the React App

Our first task is to create a new React project. We could do this manually, but that would take too long. Instead, we’re using a tool the React team at Facebook have created called Create React App.

Scaffolding with Create React App

Go to the Create React App Github repository page and follow the instructions on setting up a new React project. Come back here when you have the base app up and running. It should look something like this:

Adding Bootstrap

Now that we have our React app working, our second task is to add Bootstrap to the codebase. For those who don’t know, Bootstrap is a component library which gives us well documented, styled HTML components such as buttons, cards, tabs, etc.

If you’re wondering what the difference between React and Bootstrap is, think of them within the context of a novel. React would be the spine and pages of the book, while Bootstrap would be the words, headings, and images.

There is a library called Reactstrap that has ‘ Reactified ‘ the bootstrap HTML components and turned them into React components, however, we’ll be using the standard Bootstrap library instead.

To install the latest Bootstrap, open up a terminal window, make sure you’re in the new React project directory, and run the following commands:

Terminal npm i bootstrap --save && npm i popper.js --save && npm i jquery --save

This command installs three libraries:

Bootstrap.

Popper.js, a bootstrap dependency that powers the tooltips and popover components.

jQuery, another bootstrap dependency that Bootstrap relies on for animations, hiding and showing of elements, amongst other things.

If all goes well, you should see a console output that looks very similar to this:

Terminal + bootstrap@4.1.3 added 1 package from 2 contributors in 18.516s [ + ] no known vulnerabilities found [ 35668 packages audited ] npm WARN bootstrap@4.1.3 requires a peer of jquery@1.9.1 - 3 but none is installed. You must install peer dependencies yourself. + popper.js@1.14.5 added 1 package from 2 contributors in 16.791s [ + ] no known vulnerabilities found [ 35669 packages audited ] + jquery@3.3.1 added 1 package from 1 contributor in 15.364s [ + ] no known vulnerabilities found [ 35670 packages audited ]

Importing Bootstrap

You’re halfway there! Next, we need to actually import bootstrap into our React app so that we can begin using it.

We want Bootstrap to be accessible from anywhere in our code. Therefore, we should import it at the root of the application.

Open up index.js and add two new import statements for the Bootstrap CSS and JS files, towards the end of the existing imports:

index.js import React from 'react' ; import ReactDOM from 'react-dom' ; import './index.css' ; import App from './App' ; import * as serviceWorker from './serviceWorker' ; import 'bootstrap/dist/css/bootstrap.css' ; import 'bootstrap/dist/js/bootstrap.js' ; ReactDOM . render ( < App /> , document . getElementById ( 'root' ) ) ; serviceWorker . unregister ( ) ;

Good stuff! Now, let’s make sure Bootstrap has been imported successfully. Open up App.js and add one line of HTML to insert a Bootstrap styled HTML button component, immediately below the ‘Learn React’ link:

App.js import React , { Component } from 'react' ; import logo from './logo.svg' ; import './App.css' ; class App extends Component { render ( ) { return ( < div className = " App " > < header className = " App-header " > < img src = { logo } className = " App-logo " alt = " logo " /> < p > Edit < code > src/App.js </ code > and save to reload. </ p > < a className = " App-link " href = " https://reactjs.org " target = " _blank " rel = " noopener noreferrer " > Learn React </ a > < button type = " button " className = " btn btn-primary " > Primary </ button > </ header > </ div > ) ; } } export default App ;

Save your changes and navigate to the running web app in your browser. If you see a blue button, similar to the screenshot below, then you’ve installed and imported Bootstrap successfully and we can move on! 👍

Creating the Base Component

When building a dynamic web app, I always like to ‘stub’ out the components first using dummy data. This involves creating the user interface and populating it using static data that I define at the top of the component. You’ll see what I mean in a few minutes.

While you’re in App.js, go ahead and delete everything in the render function, as well as the last two import statements at the top of the file.

Then, add a Bootstrap container, row, and column class. If you don’t know the basics of defining layout in Bootstrap, I highly suggest you check out their overview on layout.

App.js import React , { Component } from 'react' ; class App extends Component { render ( ) { return ( < div className = " container mt-5 " > < div className = " row " > < div className = " col " > ... </ div > </ div > </ div > ) ; } } export default App ;

Don’t hit that save button just yet! We need to add our card HTML components inside the column class. For now, we’ll add three cards:

App.js import React , { Component } from 'react' ; class App extends Component { render ( ) { return ( < div className = "container mt-5" > < div className = "row" > < div className = "col" > < div className = "card-deck" > < div className = "card" > < img className = "card-img-top" src = "https://via.placeholder.com/362x200" alt = "Card image cap" / > < div className = "card-body" > < h5 className = "card-title" > Card title < / h5 > < p className = "card-text" > This is a longer card with supporting text below as a natural lead - in to additional content . This content is a little bit longer . < / p > < p className = "card-text" > < small className = "text-muted" > Last updated 3 mins ago < / small > < / p > < / div > < / div > < div className = "card" > < img className = "card-img-top" src = "https://via.placeholder.com/362x200" alt = "Card image cap" / > < div className = "card-body" > < h5 className = "card-title" > Card title < / h5 > < p className = "card-text" > This card has supporting text below as a natural lead - in to additional content . < / p > < p className = "card-text" > < small className = "text-muted" > Last updated 3 mins ago < / small > < / p > < / div > < / div > < div className = "card" > < img className = "card-img-top" src = "https://via.placeholder.com/362x200" alt = "Card image cap" / > < div className = "card-body" > < h5 className = "card-title" > Card title < / h5 > < p className = "card-text" > This is a wider card with supporting text below as a natural lead - in to additional content . This card has even longer content than the first to show that equal height action . < / p > < p className = "card-text" > < small className = "text-muted" > Last updated 3 mins ago < / small > < / p > < / div > < / div > < / div > < / div > < / div > < / div > ) ; } } export default App ;

Each card component is filled with static content for now, so we can see what our app will look like once it’s being populated with dynamic data from Airtable.

Now you can hit that save button! Once it’s refreshed, you should see something that looks like this:

Stubbing out the Base Component

Now that we have three static movie cards looking nice in our component, we can begin to ‘stub’ out the data. When stubbing out the data, I do the following:

Create a temporary array of objects which mimics the structure of the data that will eventually be passed into this component from our database via an API. In this case, Airtable.

Turn the static HTML components into a new React component, so that I can dynamically render it.

Loop through the array of objects, rendering out the new React component for each object.

Let’s start with the first step. At the top of our App.js component, right after the import statement, add the following array of objects:

App.js const movieData = [ { title : 'Avengers: Infinity War' , year : '2018' , description : 'Iron Man, Thor, the Hulk and the rest of the Avengers unite to battle their most powerful enemy yet -- the evil Thanos. On a mission to collect all six Infinity Stones, Thanos plans to use the artifacts to inflict his twisted will on reality.' , imageURL : 'https://via.placeholder.com/362x200' , } , { title : 'Bohemian Rhapsody' , year : '2018' , description : 'Bohemian Rhapsody is a foot-stomping celebration of Queen, their music and their extraordinary lead singer Freddie Mercury. Freddie defied stereotypes and shattered convention to become one of the most beloved entertainers on the planet.' , imageURL : 'https://via.placeholder.com/362x200' , } , { title : 'The Incredibles 2' , year : '2018' , description : 'Everyone’s favorite family of superheroes is back in “Incredibles 2” – but this time Helen is in the spotlight, leaving Bob at home with Violet and Dash to navigate the day-to-day heroics of “normal” life.' , imageURL : 'https://via.placeholder.com/362x200' , } , ] ;

What we’re doing here is instantiating a new constant of type array. Inside this array are three objects, each with a name, year, description, and image_url key/value pairs.

Creating the MovieCard Component

This is where the fun begins…

We’ve checked off step one: stubbing out our component with dummy data.

If you remember our three steps from before, step two was converting the static HTML components into a React component. To do that, at the bottom of App.js after the export statement, let’s define a new stateless, functional React component called MovieCard:

MovieCard.js const MovieCard = ( ) => ( < div className = "card" > < img className = "card-img-top" src = "https://via.placeholder.com/362x200" alt = "Card image cap" / > < div className = "card-body" > < h5 className = "card-title" > Card title < / h5 > < p className = "card-text" > This is a wider card with supporting text below as a natural lead - in to additional content . This card has even longer content than the first to show that equal height action . < / p > < p className = "card-text" > < small className = "text-muted" > Last updated 3 mins ago < / small > < / p > < / div > < / div > ) ;

Remember, all React component names are written in upper camel case (pascal case). For example: MovieCard, TableList, and SideBar.

A stateless functional component has no state, and its props are passed into it through parameters.

Our component has no props. Let’s go ahead and add these to the parameter list. We’ll also add the props in the HTML so we can begin to populate our HTML component with the values that are passed into the props.

MovieCard.js const MovieCard = ( { title , year , description , imageURL } ) => ( < div className = " card " > < img className = " card-img-top " src = { imageURL } alt = " Movie poster " /> < div className = " card-body " > < h5 className = " card-title " > { title } </ h5 > < p className = " card-text " > { description } </ p > < p className = " card-text " > < small className = " text-muted " > { year } </ small > </ p > </ div > </ div > ) ;

Looping Through the Array

Now for the magic part! Our third and final step for stubbing out our component is to loop through the array of objects and render a new copy of our MovieCard component for each object in that array.

Begin by deleting the static HTML card components from the render function, keeping the outer .card-deck div, like so:

App.js class App extends Component { render ( ) { return ( < div className = " container mt-5 " > < div className = " row " > < div className = " col " > < div className = " card-deck " > ... </ div > </ div > </ div > </ div > ) ; } }

Finally, inside the .card-deck div, loop through the temporary movieData array we defined earlier and render a new copy of our <MovieCard /> component for each object in the movieData array.

App.js class App extends Component { render ( ) { return ( < div className = " container mt-5 " > < div className = " row " > < div className = " col " > < div className = " card-deck " > { movieData . map ( movie => < MovieCard { ... movie } /> ) } </ div > </ div > </ div > </ div > ) ; } }

The line of code we inserted above is doing quite a lot for one line of code.

We’re looping through our movieData array.

For each object in that array, we’re returning our <MovieCard /> component.

Finally, we’re passing in the entire movie object into the <MovieCard /> component as props.

Save the file, switch to your web browser and you should see something similar to before, but this time the cards are being populated by the data defined in our array!

Play around with the array data for a while so you can see how the data is populating our MovieCard components. Try changing the data in the array. Add more objects to the movieData array to see more cards being added to our web app.

I suggest you take a minute to go over what we’ve covered so far before moving onto the next section!

Setting up Airtable

The final piece of the React and Airtable web app puzzle — Plugging an Airtable database into our quite static front end!

Sign up for a new Airtable account if you haven’t already.

I’d really appreciate it if you used my referral link: https://airtable.com/invite/r/w7plpPSM.

(Full disclosure: after signing up, I receive a $10 credit towards my Airtable account.)

You should be redirected to the dashboard page where you can create a new base after creating a new account. A base is Airtables name for a database.

Click the ‘Add a base’ button and then ‘Start from scratch’.

Give your new base a name (I chose ‘Movies’ — it felt appropriate), change the color, and pick a funky icon. Once you’re done customizing your base, click into it.

Customizing the Airtable Base

An empty base always starts with three columns: ‘Name’, ‘Notes’, and ‘Attachments’. We want to store data about our favorite movies. Change the current column structure by:

Rename ‘Table 1’ to ‘favorites’.

Delete the ‘Notes’ and ‘Attachments’ columns.

Rename the ‘Name’ column to ‘title’ (use lowercase!).

Create a new column of type number and name it ‘year’.

Add a new column of type long text and name it ‘description’.

Create a new column of type attachment and name it ‘ imageURL ‘.

Airtable has an incredibly intuitive UI, so I haven’t given step-by-step instructions on how to accomplish everyting . Take some time to explore it!

You can start adding in the movie data once you have added these columns. Feel free to add new movies, or copy the data from the movieData array we defined earlier in the tutorial.

You’ll have to find images of the movie posters online so you can upload them to the ‘imageURL’ column.

Accessing the Airtable Bases’ API

What makes Airtable so great for developers is their API. Each base provides its own very well documented API, allowing you to add, delete, edit, and save data from within your own apps. How great is that?!

The best part is the API is already set up for you, and it’s absolutely free!

While you’re still signed in, go to https://airtable.com/api.

Find your base and click on it. In front of you is some rather lengthy technical documentation that describes how you interact with the Airtable API.

Scroll down to ‘Authentication’ and look at the code examples in the dark section to the right of the screen. We’re interested in the line of code under ‘EXAMPLE USING QUERY PARAMETER’.

Copy and paste just the URL somewhere for now, or make a note of it. We will need this very soon. Make sure you click the ‘Show API Key’ checkbox in the upper right-hand corner.

https://api.airtable.com/v0/appgOPm5an5ZzNvkk/favorites?api_key = YOUR_API_KEY

Integrating React and Airtable

Next let’s bring the data from Airtable into our React app.

An API (short for Application Programming Interface) is a way for software to communicate with each other. For example, our React web app wants to know what movies are stored in our Airtable database. Airtable provides us with API documentation which tells us what URLs we must talk to from our React web app.

Open your text editor and go to the App.js component.

Let’s add state to our component. State is for storing data that changes, such as a ticking timer, number of clicks, or responses from API calls.

Define an initial state inside of a constructor method, like so:

App.js class App extends Component { constructor ( props ) { super ( props ) ; this . state = { movies : [ ] , } ; } ... }

We’re setting the initial state of the App component to be an object with an empty array named ‘movies’.

Add another method called componentDidMount directly underneath this new constructor method.

App.js ... componentDidMount ( ) { fetch ( 'https://api.airtable.com/v0/appgOPm5an5ZzNvkk/favorites?api_key=YOUR_API_KEY' ) . then ( ( resp ) => resp . json ( ) ) . then ( data => { this . setState ( { movies : data . records } ) ; } ) . catch ( err => { } ) ; } ...

componentDidMount is a lifecycle method of a class component. The code inside of it is run whenever a component is ready to be loaded onto the page.

The code above does several things:

It adds the componentDidMount method, so that code is run once the component is ready to be shown on the web page.

It uses fetch to call the Airtable API and get our list of movies we added to it earlier.

It updates the component state key ‘movies’ to be the data coming back from Airtable.

Remember to use your Airtable API_KEY and code I asked you to save earlier or this won’t work!

Using our Airtable Data

Finally, change the render method to use our new ‘movies’ array stored in the component state:

App.js ... render ( ) { return ( < div className = " container mt-5 " > < div className = " row " > < div className = " col " > < div className = " card-deck " > { this . state . movies . map ( movie => < MovieCard { ... movie . fields } /> ) } </ div > </ div > </ div > </ div > ) ; } ...

We need to modify the MovieCard component so that the HTML image component src attribute points to the correct value in the object:

MovieCard.js const MovieCard = ( { title , year , description , imageURL } ) => ( < div className = " card " > < img className = " card-img-top " src = { imageURL [ 0 ] . url } alt = " Movie poster " /> < div className = " card-body " > < h5 className = " card-title " > { title } </ h5 > < p className = " card-text " > { description } </ p > < p className = " card-text " > < small className = " text-muted " > { year } </ small > </ p > </ div > </ div > ) ;

Save and check out the running web app in your browser. You should see some really great looking, data-driven movie cards!

The final component is below:

App.js import React , { Component } from 'react' ; class App extends Component { constructor ( props ) { super ( props ) ; this . state = { movies : [ ] , } ; } componentDidMount ( ) { fetch ( 'https://api.airtable.com/v0/YOUR_AIRTABLE_ENDPOINT?api_key=YOUR_API_KEY' ) . then ( ( resp ) => resp . json ( ) ) . then ( data => { console . log ( data ) ; this . setState ( { movies : data . records } ) ; } ) . catch ( err => { } ) ; } render ( ) { return ( < div className = " container mt-5 " > < div className = " row " > < div className = " col " > < div className = " card-deck " > { this . state . movies . map ( movie => < MovieCard { ... movie . fields } /> ) } </ div > </ div > </ div > </ div > ) ; } } export default App ; const MovieCard = ( { title , year , description , imageURL } ) => ( < div className = " card " > < img className = " card-img-top " src = { imageURL [ 0 ] . url } alt = " Movie poster " /> < div className = " card-body " > < h5 className = " card-title " > { title } </ h5 > < p className = " card-text " > { description } </ p > < p className = " card-text " > < small className = " text-muted " > { year } </ small > </ p > </ div > </ div > ) ;

Wrapping Up React and Airtable

There you have it. We created a dynamic web app using React and Airtable.

I hope you enjoyed going through this tutorial as much as I did writing it!

As always, leave a comment if you run into any issues, or if you want more React and Airtable tutorials.

See you next time!