Monica Powell
Posted on April 8, 2021
I recently added an Instagram timeline to my site In Digital Color and wanted to share how I added the Instagram integration to my NextJS site and document some of the hiccups I encountered along the way with authentication.
Screenshot of Instagram Timeline integration on indigitalcolor.com
Install and import instagram-web-api
We'll use the instagram-web-api
npm package to do the heavy lifting for the integration. which should be installed within a NextJS site with npm install instagram-web-api
or yarn add instagram-web-api
. Once the package is successfully installed it should be imported into index.js
file (or whichever page you would like the Instagram feed to appear on) with:
import Instagram from "instagram-web-api"
At this point the index.js
file where I imported instagram-web-api
looks like this:
import Instagram from "instagram-web-api"
import Layout from "../components/layout"
export default function Index() {
return <Layout>{/*Other Page Content*/}</Layout>
}
Setup getStaticProps to Fetch Data at Build Time
Next, we need to set up a way to get fetched data into our component. In this case, I chose to use the async getStaticProps
function to pass data into our component.getStaticProps
is a function available to pages within NextJS that allows data to be fetched at build time and passed into page-level components.
import Layout from "../components/layout"
import Instagram from "instagram-web-api"
// empty array of instagram posts is being
// passed as a prop into the Index component
export default function Index({ instagramPosts }) {
return <Layout>{/*Other Page Content*/}</Layout>
}
export async function getStaticProps(context) {
// set posts to an empty array as a placeholder
let posts = []
return {
props: {
// return the posts as the prop instagramPosts
// for the Index function to use
instagramPosts: posts,
},
}
}
Setup Instagram Client Authentication
Before we can retrieve data from Instagram we should set up authentication instagram-web-api
in getStaticProps()
to be able to fetch data from Instagram. We can accomplish this by setting up a new Instagram client with new Instagram({username: IG_USERNAME, password: IG_PASSWORD})
and attempting to log in with await client.login()
export async function getStaticProps(context) {
// create a new client to communicate with Instagram
// this service requires authentication
//with username and password parameters
const client = new Instagram({
username: process.env.IG_USERNAME,
password: process.env.IG_PASSWORD,
})
let images = []
try {
// attempt to log in to Instagram
await client.login()
} catch (err) {
// throw an error if login to Instagram fails
console.log("Something went wrong while logging into Instagram", err)
}
return {
props: {
instagramPosts: images,
},
}
}
Credentials such as the password should be accessed via environment variables that are only available locally and at build time to avoid exposing this information. In NextJS, local environment variables can be accessed by creating a file entitled .env.development.local
. Although the username is not a secret you can add it to the environment file if you'd like. env.development.local
should be added to your .gitignore
file to avoid committing it to version control. Separately, I set up my hosting to have access to the appropriate secrets at build time which supersedes the values in the .env
file.
My env.development.local
file looks like this:
IG_USERNAME="super_cool_username"
IG_PASSWORD="super_secret_secure_password"
In addition to the env.development.local
at a minimum you should have a .env
file that sets the defaults for env variables. My .env
file looks like and is checked into version control:
IG_USERNAME=""
IG_PASSWORD=""
Confirm Instagram Client is Functional
If you're not already you should run the development server of your NextJS site with npm run next
and check in the terminal that is running the server to see if there are any console errors. You might encounter the error log we added of "Something went wrong while logging into Instagram" during this stage of the process.
Resolving 4xx Authentication Errors
While I was setting up the Instagram client with the correct credentials I encountered various 4xx authentication relates errors (if you're not familiar with HTTP status codes check out my site https://www.httriri.com/) being returned to Instagram saying "Please wait a few minutes before trying again". I was able to temporarily circumnavigate the 4xx errors and "Please wait a few minutes before trying again" message by switching my IP address with a VPN (Virtual private network) and attempting to authenticate again. A VPN allows you to access the internet with a remote VPN server's IP address as opposed to your local IP address. This article shares other ways you can change your IP address. I already had a VPN configured on my computer so I found that to be the most straightforward solution in my case. Using a VPN to access the internet resolved the issues I was encountering as they were IP address level issues. You can check if your issues are IP level by trying to log into Instagram manually on the same IP address and on another device that is connected to a separate cellular network (and has its own IP). I am wondering if there's a more official way to get Instagram to recognize API usage as not being unusual activity. If you are running into a 4xx error for a different reason this is a good time to manually confirm that the credentials you are providing are correct.
Request Timeline Data from Instagram
Once authentication is successful we can request specific data from Instagram calling client.getPhotosByUsername({username: process.env.IG_USERNAME})
to fetch timeline data for the username that is passed in.
export async function getStaticProps(context) {
const client = new Instagram({
username: process.env.IG_USERNAME,
password: process.env.IG_PASSWORD,
})
let posts = []
try {
await client.login()
// request photos for a specific instagram user
const instagram = await client.getPhotosByUsername({
username: process.env.IG_USERNAME,
})
if (instagram["user"]["edge_owner_to_timeline_media"]["count"] > 0) {
// if we receive timeline data back
// update the posts to be equal
// to the edges that were returned from the instagram API response
posts = instagram["user"]["edge_owner_to_timeline_media"]["edges"]
}
} catch (err) {
console.log(
"Something went wrong while fetching content from Instagram",
err
)
}
return {
props: {
instagramPosts: posts, // returns either [] or the edges returned from the Instagram API based on the response from the `getPhotosByUsername` API call
},
}
}
Render Instagram Data with InstagramFeed component
Once we've successfully received timeline data back from the getPhotosByUsername()
call we can render this data. Below is the skeleton I used to transform the data returned from the Instagram API into a digestible and visually engaging component that displays recent photos and links to them on Instagram so that visitors can click through to the actual posts.
import Link from "next/link"
export default function InstagramFeed({ instagramPosts }) {
return (
<>
<h2>
<a href="https://www.instagram.com/yourinstagramhandle/">
Follow Us on Instagram
</a>
.
</h2>
<ul>
{/* let's iterate through each of the
instagram posts that were returned
from the Instagram API*/}
{instagramPosts.map(({ node }, i) => {
return (
// let's wrap each post in an anchor tag
// and construct the url for the post using
// the shortcode that was returned from the API
<li>
<a
href={`https://www.instagram.com/p/${node.shortcode}`}
key={i}
aria-label="view image on Instagram"
>
{/* set the image src equal to the image
url from the Instagram API*/}
<img
src={node.thumbnail_src}
alt={
// the caption with hashtags removed
node.edge_media_to_caption.edges[0].node.text
.replace(/(#\w+)+/g, "")
.trim()
}
/>
</a>
</li>
)
})}
</ul>
</>
)
}
In order to actually render this component and the associated post data from Instagram we need to import InstagramFeed
into index.js
and render it withing the Index
component wiith the instagramPosts
prop we received from getStaticProps
.
import Layout from "../components/layout"
import Instagram from "instagram-web-api"
import InstagramFeed from "../components/instagramFeed"
// empty array of instagram posts is being
// passed as a prop into the Index component
export default function Index({ instagramPosts }) {
return (
<Layout>
{/*Other Page Content*/}
<InstagramFeed instagramPosts={instagramPosts} />
</Layout>
)
}
If everything worked properly you should now see all of your latest Instagram posts rendered on the page and be able to click any given posts to be taken to it on Instagram. If you are not seeing any posts rendering there may have been an issue with authentication (invalid credentials or rate limited by Instagram). If so, I recommend following my above troubleshooting steps of manually verifying credentials (in case of invalid credentials) or exploring using a VPN (if your IP address has been rate limited).
Posted on April 8, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.