I'm going to show you how to build websites and apps, using Nextjs and Airtable as a CMS/API/Database.
Project
We will be building a Seth Godin style blog, see here. The short posts are displayed on the home page. A note style blog. If you don't want to make a blog, pick other data you want to display. Displaying data from our Airtable is the important part.
Why Airtable?
Airtable is a spreadsheet app, with extra bits. It's easy to setup and perfect for creating your own little side projects. Create your own API without having to learn backend. Airtable has a mobile app, which means you can update your database on the go.
Why Nextjs?
Nextjs allows you to write React as normal. But with the option of static sites for improved speed and SEO improvements. We will be using nextjs's static site feature to create our site at build time. If you are new to nextjs, don't worry. It's easy to do this, and the syntax will look familiar to you.
Setup Airtable CMS
We need data first. Let's create a database for adding our posts.
Go to airtable.com and create a 'base'.
In your base rename the table and fields. I prefer to name things in lowercase to avoid and trouble further on.
Make sure you enable rich text formatting. This allows you to add lists, headers and other text formatting options.
Let's add dummy data for three fake posts.
See I have added a header, list and a word in bold, in my dummy posts. This is for working on formatting in our app. We will be converting the markdown to html.
Nextjs
In a terminal use the command npx create-next-app your-project-name-here
. This will give us nextjs's boiler plate. If you've used create-react-app
this will be familiar. You get a good starting point.
Open it up in your text editor. I'm using VScode. Theres an index.js
file in the pages
folder. This is our main for displaying.
Open a terminal. Top tip: If you're in VScode, use the command ctrl ~
to open or close the in house terminal. The command npm run dev
will start our nextjs app in a server.
You should see the below on localhost:3000
If you see this, you're good to go. Before doing anything, clean up your code. Delete the api folder along with, style.modules. And clean up the index.js code. Mine looks like this:
import Head from 'next/head';
export default function Home() {
return (
<div>
<Head>
<title>Godin style blog demo</title>
<link rel='icon' href='/favicon.ico' />
</Head>
<main>
<h1>Godin style blog demo</h1>
</main>
</div>
);
}
Airtable API
We need to connect our base to our nextjs app. Go to airtable.com/api
You will be treated with this page:
Go into the base you're using. In there you will find Airtables, fantastic docs.
The part we are interested is on the JavaScript tab, under authentication.
Don't worry if this looks alien to you. We will be using, airtables npm package to complete our requests. Go ahead and npm install airtable
.
Hiding secrets
In our code create a .env
file at the top of directory. This is where we hide our environment variables in nextjs.
In .env
add your secrets.
AIRTABLE_API_KEY=YOUR_KEY_HERE
AIRTABLE_BASE_ID=YOUR_BASE_ID_HERE
AIRTABLE_TABLE_NAME=YOUR_TABLE_HERE
When you have added your secrets, make sure .env
is added to .gitignore
. You don't wan to share these on Github.
Important - restart your server for nextjs to pick up your environment variables.
Getting our data
We will create a file for fetching our data. Create a lib
folder and within that a getPosts.js
file. This is where our airtable code will go, for handling data fetching.
In getPosts.js
add the following code. The code connects to airtable and fetches the records form the base you have provided.
const Airtable = require('airtable');
const base = new Airtable({
apiKey: process.env.AIRTABLE_API_KEY,
}).base(process.env.AIRTABLE_BASE_ID);
const table = base(process.env.AIRTABLE_TABLE_NAME);
export default async function getPosts() {
const records = await table.select({}).all();
console.log(records);
return records;
}
To see if this works, call the getPosts()
function in index.js
. In your terminal you will see a tonne of information. With this data bombardment, you will see fields
. This is the data we need to display.
In getPosts()
we will create two functions for minifying the data and giving us what we need.
const Airtable = require('airtable');
const base = new Airtable({
apiKey: process.env.AIRTABLE_API_KEY,
}).base(process.env.AIRTABLE_BASE_ID);
const table = base(process.env.AIRTABLE_TABLE_NAME);
// maps over the records, calling minifyRecord, giving us required data
const getMinifiedRecords = records => {
return records.map(record => minifyRecord(record));
};
// gets the data we want and puts it into variables
const minifyRecord = record => {
return {
id: record.id,
fields: record.fields,
};
};
export default async function getPosts() {
const records = await table.select({}).all();
const minifiedRecords = await getMinifiedRecords(records);
console.log(minifiedRecords);
return minifiedRecords;
}
Remember check in your terminal for the console.log
. As of writing this, my browser is giving an error, requesting an API key.
Displaying content
We have our content in the console, we need to display this. We will be using nextjs's getStaticProps()
built in function for getting the content on build time.
We do this in index.js
because that's our page being rendered. We add out getStaticProps()
function at the bottom of our file.
export async function getStaticProps() {
const posts = await getPosts();
return {
props: {
posts,
},
};
}
To give the home function access to the data we pass it in as props.
export default function Home({ posts }) {
To see the data displayed on the DOM, map over it as you would in any React app.
import Head from 'next/head';
import getPosts from '../lib/getPosts';
export default function Home({ posts }) {
return (
<div>
<Head>
<title>Godin style blog demo</title>
<link rel='icon' href='/favicon.ico' />
</Head>
<main>
<h1>Godin style blog demo</h1>
<ul>
{posts.map(post => (
<li key={post.id}>
<h3>{post.fields.title}</h3>
<time>{post.fields.published}</time>
<div>{post.fields.content}</div>
</li>
))}
</ul>
</main>
</div>
);
}
export async function getStaticProps() {
const posts = await getPosts();
return {
props: {
posts,
},
};
}
If all has gone to plan you should, your posts.
One thing we need to add to getStaticProps()
. Because this data is given to us on build time, means if we add a new post. It won't be added, until you rebuild the site. But nextjs gives us a way to fix this. Adding revalidate: 1
to our function, makes nextjs look for anything new added. If you add a new post. Nextjs will pick it up. The number added is how frequent you want nextjs to check. Your function should look like this.
export async function getStaticProps() {
const posts = await getPosts();
return {
props: {
posts,
revalidate: 1,
},
};
}
Formatting our text
Our rich text from airtable is given to us as markdown. You will see this displayed on the DOM as '*','###'. We want this to be turned into html.
We will be using the package marked for this. Marked is easy to use. Install one package ‌npm install marked
. Next wrap the markdown content, we want to display in a marked()
function.
Import our package in index.js
import marked from 'marked'
.
Before we displayed our content in a <div>
. We will change that to display our content in HTML. Using dangerouslySetInnerHTML
and wrapping our post.fields.content
in marked()
.
<div
dangerouslySetInnerHTML={{
__html: marked(post.fields.content),
}}
/>
Once your index page looks like this
import Head from 'next/head';
import marked from 'marked';
import getPosts from '../lib/getPosts';
export default function Home({ posts }) {
return (
<div>
<Head>
<title>Godin style blog demo</title>
<link rel='icon' href='/favicon.ico' />
</Head>
<main>
<h1>Godin style blog demo</h1>
<ul>
{posts.map(post => (
<li key={post.id}>
<h3>{post.fields.title}</h3>
<time>{post.fields.published}</time>
<div
dangerouslySetInnerHTML={{
__html: marked(post.fields.content),
}}
/>
</li>
))}
</ul>
</main>
</div>
);
}
export async function getStaticProps() {
const posts = await getPosts();
return {
props: {
posts,
revalidate: 1,
},
};
}
Have a look at your app. You should see that the markdown has been formatted and changed to HTML. Magic.
Conclusion
We have built a 'Seth Godin' style, thoughts blog. Made with nextjs and Airtable. Airtable is great for templating a database or using as a CMS. It's powerful.
You can update your CMS any time with Airtable's mobile app.
Nextjs is versatile, and is great for static sites due to its readable syntax. Unlike other frameworks that require graphQL knowledge.
I wish I knew about using Airtable as an API months back. It would of helped with getting ideas out, without the bother of backends or proper databases.
Where to go from here
- The app needs styling. I will continue, this app by writing an article on adding TailwindCSS for styling.
- We need to host this site. I will write an article on hosting on Vercel
- Add dynamic routing with nextjs.
- SEO improvements
If your'e new to nextjs, it's worth looking at the docs. They're a rare example of decent documentation.
Project ideas using airtable + nextjs
Theres many things you can make using airtable and nextjs. They're good for practise. A few to get you started:
- Favourite quotes app
- Nomad List style site
- One sentence a day journal
Top comments (3)
Just a typo, you are not positioning the
revalidate
value correctly. See here:😇
hi David,
are u didn't facing this issue?
i try to clone your project n run it
Error: An API key is required to connect to Airtable
This is related to calling getPosts outside getStaticProps, since you are not able to read process.env.FOO client side.