All Posts

Displaying an Instagram feed with Gatsby, part 2

In Part 1, we set up the pieces we needed to pull our content from Instagram’s API and include images in a page in our Gatsby static site. In this tutorial, we’ll make our Instagram component display a little nicer within our pages.

For Part 2, you’ll need:

  • A grid system that works with your Gatsby starter, or
  • Some knowledge of CSS Grid

Putting posts into a grid

The Instagram display on my About page is based off of the stylesheet and layout techniques my Gatsby starter uses, which is SCSS Modules (CSS Modules that use SCSS syntax) and Lost Grid.

You’ll need to have a grid layout solution that works with your own site’s Gatsby starter, or, you can use the CSS Grid layout example I’ve provided further down in this post.

Here’s a refresher of the component JSX that we wrote in Part 1 to load up our Instagram posts into a basic container.

// Instagram.js
import React from 'react';
import { graphql, StaticQuery } from 'gatsby';
import Image from 'gatsby-image';

const Instagram = () => (
  <StaticQuery
    query={graphql`
      query InstagramPosts {
        allInstagramContent(limit: 12) {
          edges {
            node {
              link
              localImage {
                childImageSharp {
                  fluid(maxHeight: 500, maxWidth: 500 quality: 50) {
                    ...GatsbyImageSharpFluid_withWebp_tracedSVG
                  }
                }
              }
              images {
                standard_resolution {
                  width
                  height
                  url
                }
                low_resolution {
                  url
                }
              }
            }
          }
        }
      }
    `}
    render={(data) => (
      <div>
        {
          data.allInstagramContent.edges.map((item, i) => (
            item.node.localImage ? (
              <div key={i}>
                <Image
                  fluid={item.node.localImage.childImageSharp.fluid}
                />
              </div>
            ) : (<div></div>)
          ))
        }
      </div>
    )}
  />
);

export default Instagram;

Before we dive into the grid layout, let’s add an anchor tag around each post thumbnail that will take the user to the full post when it’s clicked.

// Instagram.js

[...]

    render={(data) => (
      <div>
        {
          data.allInstagramContent.edges.map((item, i) => (
            item.node.localImage ? (
              <div key={i}>
                <a
                  href={item.node.link}
                  target='_blank'
                  rel='noopener'
                  tabIndex='0'
                >
                  <Image
                    fluid={item.node.localImage.childImageSharp.fluid}
                  />
                </a>
              </div>
            ) : (<div></div>)
          ))
        }
      </div>
    )}
  />
);

Starter grid approach

If I look at the Page.js component that came with my Gatsby starter, I can see that the author has set the styles up like this:

// Page.js

import React, { useRef, useEffect } from 'react';
import styles from './Page.module.scss';

const Page = ({ title, children }) => {
  [...]

  return (
    <div ref={pageRef} className={styles['page']}>
      <div className={styles['page__inner']}>
        [...]
      </div>
    </div>
  );
};

export default Page;

As you can see, there’s a Page.module.scss file that’s being imported at the top; this contains the styles for this component. The component has its own page class prefix on the outermost div; elements nested inside of it use page__* as a prefix (in keeping with BEM methodology).

Here’s a glimpse into how the styles are structured within Page.module.scss:

// Page.module.scss

.page {

   &__inner {
     padding: 25px 20px;
   }
}

1) Add classes

In the Instagram.js file, I’ll add the relevant className properties to the elements within my component:

// Instagram.js

[...]

      <div className={styles['instagram']}>
        {
          data.allInstagramContent.edges.map((item, i) => (
            item.node.localImage ? (
              <div key={i} className={styles['instagram__post']}>
                <a
                  className={styles['instagram__post-link']}
                  href={item.node.link}
                  target='_blank'
                  rel='noopener'
                  tabIndex='0'
                >
                  <Image
                    fluid={item.node.localImage.childImageSharp.fluid}
                  />
                </a>
              </div>
            ) : (<div></div>)
          ))
        }
      </div>

2) Create and import stylesheet file

I’m going to mirror the stylesheet approach with my Instagram component by creating a new Instagram.module.scss file within the component’s folder:

// Instagram.module.scss

.instagram {
  @include margin-bottom(1);
  lost-utility: clearfix;

  &__post {
    lost-column: 1/4 4 0;
  }
}

…and then I’ll import that stylesheet into my Instagram.js file.

// Instagram.js

import React from 'react';
import { graphql, StaticQuery } from 'gatsby';
import Image from 'gatsby-image';
import styles from './Instagram.module.scss';

[...]

Now my display uses a grid that’s 4 columns wide, and it works great on both desktop and mobile, so I don’t need to use any media queries to affect the layout!

CSS Grid approach

If my Gatsby starter didn’t have an out-of-the-box layout solution, then CSS Grid would be easy an easy solution to implement within my component’s inline styles.

// Instagram.js

import React from 'react';
import { graphql, StaticQuery } from 'gatsby';
import Image from 'gatsby-image';

const Instagram = () => (
  <StaticQuery
    query={graphql`
      query InstagramPosts {
        allInstagramContent(limit: 12) {
          edges {
            node {
              link
              localImage {
                childImageSharp {
                  fluid(maxHeight: 500, maxWidth: 500 quality: 50) {
                    ...GatsbyImageSharpFluid_withWebp_tracedSVG
                  }
                }
              }
              images {
                standard_resolution {
                  width
                  height
                  url
                }
                low_resolution {
                  url
                }
              }
            }
          }
        }
      }
    `}
    render={(data) => (
      <div style={{
        marginBottom: '1rem',
        display: 'grid',
        gridTemplateColumns: 'repeat(4, 1fr)',
      }}>
        {
          data.allInstagramContent.edges.map((item, i) => (
            item.node.localImage ? (
              <div key={i}>
                <a
                  href={item.node.link}
                  target='_blank'
                  rel='noopener'
                  tabIndex='0'
                >
                  <Image
                    fluid={item.node.localImage.childImageSharp.fluid}
                  />
                </a>
              </div>
            ) : (<div></div>)
          ))
        }
      </div>
    )}
  />
);

export default Instagram;

The resulting display layout should look something like this:

Instagram posts loaded into a 4-column grid
Instagram posts loaded into a 4-column grid

What’s next

In Part 3 (coming soon!), I’ll demonstrate how to add the component into your Markdown files. I’ll also list some housekeeping tasks and gotchas that you may encounter as a result of implementing this component on your site.