Airtable is an online service that provides convenient data storage in spreadsheet format. Even though it looks and feels like a simple spreadsheet editor, you can actually use it to build powerful, dynamic applications. Without going through the process of building and managing a server, you can use Airtable's API like a database and plug in any of your frontend libraries.

This tutorial will showcase you how easy it is to manage data on an Airtable spreadsheet. Then, by using Airtable's API, you're going to create a React application that will fetch data from the API. By the end of this tutorial, you will have a complete idea of how to create React apps and use Airtable to store data in the cloud.

Instead of going into too many details about CSS styles, we'll learn how to use Material UI library in a React app. This library makes it easy to create pleasing React UI components.

While working with this tutorial, do note that the Airtable API has some rate limitations. The Airtable API has a limit of 5 requests per second. This is quite reasonable to work with. If you exceed this rate, you will receive a 429 status code and will need to wait 30 seconds before sending subsequent requests that will succeed.

Table of Contents

  • Requirements
  • Setting up a New React app
  • Adding Material UI to React App
  • Create a Mock App
  • Loop through the Mock Data
  • Setting up Airtable
  • Fetching the Data from the Airtable API
  • Connect Crowdbotics platform to host your React + Airtable App
  • Conclusion

Requirements

  • Nodejs v8.x.x or higher installed along with npm/yarn.
  • create-react-app installed globally on your local dev machine to generate a new React project.
  • Free Airtable account and API key.
Bonus: You can also use npx to generate a new React project without installing create-react-app.

Setting up a New React app

To get started, you will have to create a new React project. When you have installed the create-react-app generator tool, make sure to run the following command from a terminal window. The following command on successful execution will create a new directory that will contain all the code required to bootstrap a React app.

create-react-app react-airtable-app

# or with npx

npx create-react-app react-airtable-app

After the project directory is created, do not forget to navigate inside it. For the rest of this tutorial, you are going to execute every command if specified and required, at the root of this directory. Right now, if you have navigated inside the project folder, you can run the command yarn start and go to a browser window to see if the default React app is working.

Placeholder React app screen in browser

Adding Material UI to React App

To build faster React components, a UI library always makes the process easier. For this demo and the tutorial, let us use Material-UI. For those of you who are not familiar, Material UI is a set of React Components that strictly follows Google's Material Design Guidelines.

Is this your first time using Material UI, please follow the steps below to set this library with your React app. If you are familiar with this library and know how to set up, you can totally skip this section.

Next, open the terminal window and install the package associated with Material UI components.

yarn add @material-ui/core

Next, to try it out, open App.js and add the following code snippet.

//App.js
import React from 'react'
import logo from './logo.svg'
import './App.css'
import Container from '@material-ui/core/Container'
import { makeStyles } from '@material-ui/core/styles'
import Card from '@material-ui/core/Card'
import CardActionArea from '@material-ui/core/CardActionArea'
import CardActions from '@material-ui/core/CardActions'
import CardContent from '@material-ui/core/CardContent'
import CardMedia from '@material-ui/core/CardMedia'
import Button from '@material-ui/core/Button'
import Typography from '@material-ui/core/Typography'

const useStyles = makeStyles({
	card: {
		maxWidth: 345
	},
	media: {
		height: 180
	}
})

function App() {
	const classes = useStyles()

	return (
		<Container maxWidth='xs'>
			<Card className={classes.card}>
				<CardActionArea>
					<CardMedia className={classes.media} image={logo} title='React' />
					<CardContent>
						<Typography gutterBottom variant='h5' component='h2'>
							React + Airtable app
						</Typography>
						<Typography variant='body2' color='textSecondary' component='p'>
							This card is built with create-react-app v3.0 and Material-ui v4.2.0
						</Typography>
					</CardContent>
				</CardActionArea>
				<CardActions>
					<Button size='small' color='primary'>
						Share
					</Button>
					<Button size='small' color='primary'>
						Learn More
					</Button>
				</CardActions>
			</Card>
		</Container>
	)
}

export default App

There are a lot of import statements in the above App component. However, do note that it saves a ton of time to write a CSS style for the output you are going to get. The Container component is the most basic UI layout element in the Material UI library. It helps to center the content of the app horizontally. The Card component itself contains a lot of elements to display a card with a media and description, just like in the above snippet.

The useStyles is an object in which you can define custom classes to use inside the component and use it as a hook. Classes as defined above card and media. Lastly, the typography allows you to override CSS properties defined for a specific React component. For more information on Typography API, you can refer to the official documentation.

Here is the output you will get for the above component.

Placeholder React + Airtable app welcome screen

Create a Mock Card

Since you have hooked up Material UI with the React app, let us complete how the overall UI is going to look with the data that is going to be stored in Airtable's part-spreadsheet, part-database storage. Start by creating a new directory called src/components, and inside this, create a new file BooksCard.js. This component will be responsible for displaying the card and the data.

Apart from importing the usual statements from @material-ui/core to create a card component, also define an object called booksData that contains data related to one book item. Later, you are going to convert this object to an array to add more books. Then, that array will be looped through to display multiple cards with their own data.

import React from 'react'

import Container from '@material-ui/core/Container'
import { makeStyles } from '@material-ui/core/styles'
import Card from '@material-ui/core/Card'
import CardActionArea from '@material-ui/core/CardActionArea'
import CardActions from '@material-ui/core/CardActions'
import CardContent from '@material-ui/core/CardContent'
import CardMedia from '@material-ui/core/CardMedia'
import Button from '@material-ui/core/Button'
import Typography from '@material-ui/core/Typography'

const booksData = {
	title: 'Pietr the Latvian',
	author: 'Georges Simenon',
	published: '1931',
	description:
		'Who is Pietr the Latvian? Is he a gentleman thief? A Russian drinking absinthe in a grimy bar? A married Norwegian sea captain? A twisted corpse in a train bathroom? Or is he all of these men? Inspector Maigret, tracking a mysterious adversary and a trail of bodies, must bide his time before the answer comes into focus.',
	coverImage:
		'https://i.gr-assets.com/images/S/compressed.photo.goodreads.com/books/1386339994l/19234594.jpg'
}

const useStyles = makeStyles({
	gutterTop: {
		marginTop: 20
	},
	card: {
		maxWidth: 345
	},
	media: {
		height: 350
	}
})

function BooksCard() {
	const classes = useStyles()

	return (
		<Container maxWidth='xs' className={classes.gutterTop}>
			<Card className={classes.card}>
				<CardActionArea>
					<CardMedia className={classes.media} image={booksData.coverImage} title='React' />
					<CardContent>
						<Typography gutterBottom variant='h5' component='h2'>
							{booksData.title}
						</Typography>
						<Typography variant='body2' color='textSecondary' component='p'>
							by {booksData.author}
						</Typography>
					</CardContent>
				</CardActionArea>
				<CardActions>
					<Button size='small' variant='outlined' color='disabled'>
						{booksData.published}
					</Button>
				</CardActions>
			</Card>
		</Container>
	)
}

export default BooksCard

This is similar to what you built in the App component in the previous section. Modify the App.js file and import this component as shown below.

import React from 'react'
import BooksCard from './components/BooksCard'

function App() {
	return <BooksCard />
}

export default App

Now visit the browser window where the development server for the current React app is serving the app. You will get the following result.

Loop through the Mock Data

Most of the component that we'll use to display different cards is done. In this section, let's add the final piece to complete the creation of the BooksCard component. This final piece will loop through data to display as many cards as the number of book objects are available. Each of the objects will contain details related to a book such as a title, name of the author, publishing year, and so on. Open BooksCard.js, and then replace the dummy data object with the following array.

const booksData = [
	{
		title: 'Pietr the Latvian',
		author: 'Georges Simenon',
		published: '1931',
		description:
			'Who is Pietr the Latvian? Is he a gentleman thief? A Russian drinking absinthe in a grimy bar? A married Norwegian sea captain? A twisted corpse in a train bathroom? Or is he all of these men? Inspector Maigret, tracking a mysterious adversary and a trail of bodies, must bide his time before the answer comes into focus.',
		coverImage:
			'https://i.gr-assets.com/images/S/compressed.photo.goodreads.com/books/1386339994l/19234594.jpg'
	},
	{
		title: 'A Moveable Feast',
		author: 'Ernst Hemingway',
		published: '1964',
		description:
			'Begun in the autumn of 1957 and published posthumously in 1964, Ernest Hemingways A Moveable Feast captures what it meant to be young and poor and writing in Paris during the 1920s. A correspondent for the Toronto Star, Hemingway arrived in Paris in 1921, three years after the trauma of the Great War and at the beginning of the transformation of Europe cultural landscape: Braque and Picasso were experimenting with cubist form; James Joyce, long-living in self-imposed exile from his native Dublin, had just completed Ulysses; Gertrude Stein held court at 27 Rue de Fleurus, and deemed young Ernest a member of une gneration perdue; and T.S. Eliot was a bank clerk in London. It was during these years that the as-of-yet unpublished young writer gathered the material for his first novel The Sun Also Rises, and the subsequent masterpieces that followed.',
		coverImage:
			'https://i.gr-assets.com/images/S/compressed.photo.goodreads.com/books/1531210888l/4631.jpg'
	}
]

To display multiple cards, first make use of the Grid component from the Material UI library. This component helps any material design layout to be responsive and is based on the flexbox algorithm. To loop through each book item from the array defined in the above code snippet, use JavaScript's map function. The looped array will first contain each Grid item, which will further include the Card component and its contents. Here is the complete snippet for BooksCard.js.

import React from 'react'
import { makeStyles } from '@material-ui/core/styles'
import Card from '@material-ui/core/Card'
import CardActionArea from '@material-ui/core/CardActionArea'
import CardActions from '@material-ui/core/CardActions'
import CardContent from '@material-ui/core/CardContent'
import CardMedia from '@material-ui/core/CardMedia'
import Button from '@material-ui/core/Button'
import Typography from '@material-ui/core/Typography'
import Grid from '@material-ui/core/Grid'

const booksData = [
	{
		title: 'Pietr the Latvian',
		author: 'Georges Simenon',
		published: '1931',
		description:
			'Who is Pietr the Latvian? Is he a gentleman thief? A Russian drinking absinthe in a grimy bar? A married Norwegian sea captain? A twisted corpse in a train bathroom? Or is he all of these men? Inspector Maigret, tracking a mysterious adversary and a trail of bodies, must bide his time before the answer comes into focus.',
		coverImage:
			'https://i.gr-assets.com/images/S/compressed.photo.goodreads.com/books/1386339994l/19234594.jpg'
	},
	{
		title: 'A Moveable Feast',
		author: 'Ernst Hemingway',
		published: '1964',
		description:
			'Begun in the autumn of 1957 and published posthumously in 1964, Ernest Hemingways A Moveable Feast captures what it meant to be young and poor and writing in Paris during the 1920s.A correspondent for the Toronto Star, Hemingway arrived in Paris in 1921, three years after the trauma of the Great War and at the beginning of the transformation of Europe cultural landscape: Braque and Picasso were experimenting with cubist form; James Joyce, long living in self-imposed exile from his native Dublin, had just completed Ulysses; Gertrude Stein held court at 27 Rue de Fleurus, and deemed young Ernest a member of une gneration perdue; and T.S. Eliot was a bank clerk in London. It was during these years that the as-of-yet unpublished young writer gathered the material for his first novel The Sun Also Rises, and the subsequent masterpieces that followed.',
		coverImage:
			'https://i.gr-assets.com/images/S/compressed.photo.goodreads.com/books/1531210888l/4631.jpg'
	}
]

const useStyles = makeStyles(theme => ({
	root: {
		flexGrow: 1,
		margin: 20
	},
	gutterTopAndBottom: {
		margin: 20
	},
	card: {
		maxWidth: 345
	},
	media: {
		height: 350
	}
}))

function BooksCard() {
	const classes = useStyles()

	return (
		<div className={classes.root}>
			<Grid container direction='row' spacing={2}>
				{booksData.map((bookItem, index) => (
					<Grid item xs={3}>
						<Card className={classes.card}>
							<CardActionArea>
								<CardMedia className={classes.media} image={bookItem.coverImage} title='React' />
								<CardContent>
									<Typography gutterBottom variant='h5' component='h2'>
										{bookItem.title}
									</Typography>
									<Typography variant='body2' color='textSecondary' component='p'>
										by {bookItem.author}
									</Typography>
								</CardContent>
							</CardActionArea>
							<CardContent>
								<Typography paragraph variant='body2'>
									{bookItem.description.length > 150
										? bookItem.description.slice(0, 150) + `...`
										: bookItem.description}
								</Typography>
							</CardContent>
							<CardActions>
								<Button size='small' variant='outlined' color='disabled'>
									{bookItem.published}
								</Button>
							</CardActions>
						</Card>
					</Grid>
				))}
			</Grid>
		</div>
	)
}

export default BooksCard

Here is the output you are going to get in the browser window.

A grid display showing two items in the grid

This completes the UI part of the React app.

Setting up Airtable

Visit this link to signup if you do not already have an account. If you do, you can just log in.

Airtable account creation screen

Once you are signed in, you will be redirected to the Dashboard screen. On this page, you can create a new base. In Airtable terminology, a base is a database. Before you proceed, generate an API key first. On the Account overview page, you will find a section named API.

Airtable API key generator

Generate your API key right now and save it somewhere. This will be needed later to access the data from the Airtable base using the Airtable API. Now, let us create a new base by clicking on the button Add a base and then click Start from scratch. Enter a name for the base, and you are set to go.

Airtable "Add a base" button

Any table in Airtable base starts with three columns in a particular table, as shown below.

Default starter database in Airtable

You are going to modify this table according to the mock (or dummy) data object that is being currently used in the React app. Also, make sure that each column is renamed in lowercase.

Airtable database with app data entered

There are going to be five columns, as shown in the above image. The first three, title, author, and published, are single-line text fields. The column description is going to be a multiple lines/long textual option. Lastly, coverImage is a string. For the value of each string, paste the URL of the image.

Fetching the Data from the Airtable API

To use the Airtable API in the React app, you are going to use JavaScript's fetch() function. This function will get the data from the Airtable base inside a lifecycle method. Open App.js and first import the Grid component from the Material UI. To keep the BooksCard component a stateless one and effectively use the custom hook useStyles from the Material UI library, let us convert App component into a class component.

import React from 'react'
import BooksCard from './components/BooksCard'
import Grid from '@material-ui/core/Grid'

class App extends React.Component {
	state = {
		booksData: []
	}

	componentDidMount() {
		fetch('https://api.airtable.com/v0/appl7QOiAVWCczxPo/Table%201?api_key=YOUR_API_KEY')
			.then(res => res.json())
			.then(res => {
				console.log(res.records)
				this.setState({ booksData: res.records })
			})
			.catch(error => console.log(error))
	}

	// render function will come here
}

export default App

After import statements and converting App into a class component, define an initial state. This state will have one property called booksData which is an array. This array is going to get an update every time the component renders. The lifecycle method componentDidMount() is responsible for fetching the data from the Airtable API.

The method fetch() takes the URL https://api.airtable.com/v0/appl7QOiAVWCczxPo/Table%201?api_key=YOUR_API_KEY. You can get this URL at this API document. For the value of the query parameter api_key, replace the value of your Airtable API key instead of YOUR_API_KEY. Using a promise, it then gets a response. The console statement inside the then() function showcases what the structure of records looks like. If you open the browser window and go to the Developer tools > Console section, you will get the following data structure.

Data structure containing the dummy data entered into the app

The data structure is an array that currently contains two objects. Each of the objects has been given an ID by the Airtable API. The data you need to use in the BooksCard component is inside fields. Lastly, the state object gets updated whenever the promise resolves with these records from the response.

The App component will render the following snippet. Inside a Grid tag, map() function is used to traverse the booksData array. Using the JavaScript ES6 syntax of the spread operator, each field is being passed as a prop to the BooksCard component.

render() {
    const {booksData} = this.state
        return (
            <Grid container direction='row' spacing={2}>
                {booksData.map(book => (
                    <BooksCard {...book.fields} key={book.fields.id} />
                ))}
            </Grid>
        )
    }

Now open the file BooksCard.js and pass all props the values that it is getting from the array, as shown in the snippet below.

function BooksCard({ title, author, published, description, coverImage }) {
	const classes = useStyles()
	return (
		<div className={classes.root}>
			<Grid item xs={10}>
				<Card className={classes.card}>
					<CardActionArea>
						<CardMedia className={classes.media} image={coverImage} title='React' />
						<CardContent>
							<Typography gutterBottom variant='h5' component='h2'>
								{title}
							</Typography>
							<Typography variant='body2' color='textSecondary' component='p'>
								by {author}
							</Typography>
						</CardContent>
					</CardActionArea>
					<CardContent>
						<Typography paragraph variant='body2'>
							{description.length > 150 ? description.slice(0, 150) + `...` : description}
						</Typography>
					</CardContent>
					<CardActions>
						<Button size='small' variant='outlined' color='primary'>
							{published}
						</Button>
					</CardActions>
				</Card>
			</Grid>
		</div>
	)
}

Save the file and go back to the browser window and you will get the following result. Each card will have its own data being displayed with a cover image, just as before.

Dummy data displaying in grid view correctly

Connect Crowdbotics platform to host your React + Airtable App

Once everything is working, now let us add git version control to this React project and then, further add the support for Crowdbotics app building platform. Open a terminal window and execute:

git init

# add all files
git add .

# commit
git commit -m "react-airtable-app"

Once all the files are committed, add this repository to your GitHub account. Crowdbotics' app building platform now gives you an option to connect a GitHub repository directly using GitHub OAuth integration (which means you need to have a Crowdbotics account or login into one using your GitHub account).

Crowdbotics GitHub repository import selector

The advantage you get from hosting your app with Crowdbotics includes technical support, easy deployments, well-maintained boilerplates for front-end development applications, and private GitHub repos. More information about this process can be found here.

Conclusion

If you have been following from the start, then you now know how to create a dynamic web application using Reactjs and Airtable as its database. If you have also used Material UI for the first time, I urge you to get in deep with it. Not only does it provides substantial React components that you can use, but it saves a lot of time when building UIs.

If you like this post, please leave a shoutout for me. I am available on Twitter, or you can check the rest of my tutorials here.