How to Add Instagram Timeline To a NextJS Site
Tags: nextjs, api, instagram
Published: 2021-04-08 00:00:36

---
title: How to Add Instagram Timeline To a NextJS Site
template: "post"
published: "true"
slug: adding-instagram-timeline-to-next-js-site
tags: ["NextJS", "API", "Instagram"]
category:
  - tutorial
description: This article walks through how to integrate Instagram on a NextJS site.
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f68hzrua03xhj2y75kbm.png
canonical_url: https://aboutmonica.com/blog/adding-instagram-timeline-to-next-js-site
---

I recently added an Instagram timeline to my site [In Digital Color](https://www.indigitalcolor.com/) 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.

![](/media/in-digital-color-ig-integration-screenshot.png)

_Screenshot of Instagram Timeline integration on indigitalcolor.com_

<TableOfContents headings={props.tableOfContents} />

## Install and import `instagram-web-api`

We'll use the [`instagram-web-api`](https://github.com/jlobos/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:

```js
import Instagram from "instagram-web-api"
```

At this point the `index.js` file where I imported `instagram-web-api` looks like this:

```js
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`](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation) 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.

```js
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()`

```js
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](https://www.comparitech.com/blog/vpn-privacy/change-ip-address/) 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.

```js
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.

```js
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`.

```js
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).