I created my first Gatsby Portfolio Starter. It shows the latest Instagram posts from any user via the Instagram API, and implements Base Web, the Design System built by Uber.
In this post, I will show you how I quickly prototyped and built this starter. I will give you tips & tricks, caveats to look for (with their workarounds), and how you can get started with your starter (See what I did there? 👀)
Click below to see the repository.
timrodz / gatsby-starter-instagram-baseweb
🎢 A lightweight, minimalist Gatsby starter for creating Instagram-based portfolios.
❓ What is Gatsby?
Gatsby is a free and open-source framework based on React that helps developers build blazing-fast websites and apps. There are lots of plugins and starters, which are like themes you can build and hack on top of, and the community is simply amazing — How great!
I have been toying around with Gatsby for over a month now. I re-designed and re-built my portfolio with it, and I'm also building a portfolio for a friend, who is a freelance photographer. This was the perfect opportunity to dive deep with Gatsby!
🤔 How does it work?
Creating this starter was unsurprisingly easy - I say this due to nature and learning curve provided by Gatsby. For context, I am a beginner with web technologies.
Finding the right template
I chose to begin using the Gatsby Default Starter. It's production-ready and is considered the vanilla starter for Gatsby. Great to learn and build upon!
Connecting to Instagram
The main feature of this website is that it can fetch and display Instagram posts. Luckily for me (and you), Gatsby has an Instagram starter! It's incredibly easy to set up and offers two ways to get started.
Public Scraping
The Instagram API offers an option to scrape up to 12 posts from any public profile. This will be the option we're going to use.
{
allInstaNode(sort: { fields: timestamp, order: DESC }, limit: 12) {
edges {
node {
id
caption
localFile {
childImageSharp {
fluid {
...GatsbyImageSharpFluid
}
}
}
}
}
}
}
Scraping using an API Token
If you want to scrape historical posts (Beyond 12), you will need credentials to authenticate with. Get started here.
This query will show us the latest 12 posts for our Instagram account, along with the id
(Will be used for redirecting to the original post), caption
and localFile
, which contains the data required to show our image.
Choosing a design system
After that, I was looking at design systems, because I wanted to create and iterate on a prototype with the least amount of setup. That's when I found Base Web, a design system created by Uber. The lightweight and minimalist approach to design made it perfect for this example.
Features:
- Robust components out of the box. From Date Pickers to simple blocks.
- Styletron for styling. It uses a CSS-in-JS approach.
- Extensibility through the Overrides API and configurable Themes.
- Built-in Accessibility.
- Great performance thanks to the Styletron engine.
Making responsive elements with BaseWeb is very easy. Here's how you would create a flexible grid that contains and shows images:
const Gallery = () => (
<FlexGrid flexGridColumnCount={[1, 2, 3]}>{renderImages()}</FlexGrid>
);
You will see flexGridColumnCount
is surrounded by an array: [1, 2, 3]
. This handles breakpoints automatically:
- Small screen size:
flexGridColumnCount = 1
- Medium screen size:
flexGridColumnCount = 2
- Large screen size:
flexGridColumnCount = 3
Before you start
There is one caveat we will have to get out the way: Gatsby uses hydration, meaning it is Server-Side-Rendered (SSR) to static content with a React runtime. If either code you use, or a plugin access variables such as window
or document
, you will have some trouble when building the app.
Let's image this is our app's entry point:
import { Client as Styletron } from 'styletron-engine-atomic';
import { Provider as StyletronProvider } from 'styletron-react';
import { LightTheme, BaseProvider, styled } from 'baseui';
import * as React from 'react';
const engine = new Styletron();
export default function EntryPoint() {
return (
<StyletronProvider value={engine}>
<BaseProvider theme={LightTheme}>
<h1>Hello!</h1>
</BaseProvider>
</StyletronProvider>
);
}
Looks simple, right? It even works via gatsby develop
. The problem lies when building the app via gatsby build
, as it will throw an error saying document is undefined
, pointing to the styletron-engine-atomic
package. Bummer! Or, is it?
If you point to the code that is breaking, you will indeed see that styletron-engine-atomic
is accessing the document
element, and this is an important part of understanding Gatsby's ecosystem. These elements only live inside the browser.
To fix this issue, we can wait until we're in the browser and then load styletron-engine-atomic
. With the magic of React hooks (useEffect
and useState
), we can tell our app to import
the library once the component mounts, meaning we are inside a browser.
import { Provider as StyletronProvider } from 'styletron-react';
import { LightTheme, BaseProvider, styled } from 'baseui';
import * as React from 'react';
export default function EntryPoint() {
const [engine, setEngine] = React.useState(null);
React.useEffect(() => {
// Once the `styletron-engine-atomic` library imports
// We will grab its content and create a new client through it
import('styletron-engine-atomic').then((styletron) => {
const clientEngine = new styletron.Client();
setEngine(clientEngine);
});
}, []);
if (!engine) return null;
return (
<StyletronProvider value={engine}>
<BaseProvider theme={LightTheme}>
<h1>Hello!</h1>
</BaseProvider>
</StyletronProvider>
);
}
With those changes in mind, this web app is ready to be built.
🏋️ Getting Gatsby to do the heavy lifting
An entry point for most Gatsby apps will be the gatsby-config.js
file. You can specify the site metadata and set up your plugins. In this case, I only grabbed a few extra plugins (besides the default ones):
- gatsby-source-instagram: Required to fetch and show data via the Instagram API.
- gatsby-plugin-styletron: Required so Base Web's engine can work along with Styletron and Gatsby. Read more.
- gatsby-plugin-alias-imports: Optional but handy tool to create shortcuts for imports, i.e.:
// -- gatsby-config.js
{
resolve: `gatsby-plugin-alias-imports`,
options: {
alias: {
components: `${__dirname}/src/components`,
data: `${__dirname}/data/`
}
}
}
// -- script.js
// Before
import { title } from '../../data/config';
import { Component } from '../components/Component';
// After
import { title } from 'data/config';
import { Component } from 'components';
Converting the app into a PWA (Progressive Web App)
Converting your app into a PWA is the hot thing, and for good reason. Google sums up what PWAs are pretty well.
With Gatsby, this is how easy it was to convert this app into a PWA:
- Enable plugin gatsby-plugin-offline inside
gatsby-config.js
. - Create a
gatsby-browser.js
file. We will need to add a callback to the onServiceWorkerUpdateReady function which will tell our application'swindow
to reload.:
export const onServiceWorkerUpdateReady = () => {
window.location.reload(true);
};
And boom - Once your website is built, you will have the basics of a PWA good to go! Here is the Google Lighthouse audit score.
Handling data
To ease things up and keep the most important variables in one place, I created a data/config.js
file. We can add things like the site's title, description, author, social links and other metadata. These variables will also power the SEO component!
SEO
I got the idea of using schema.org organizations from Smakosh.
import Thumbnail from 'static/images/thumbnail.png';
import {
address,
contact,
foundingDate,
legalName,
logo,
socialLinks,
url
} from 'data/config';
const structuredDataOrganization = `{
"@context": "http://schema.org",
"@type": "Organization",
"legalName": "${legalName}",
"url": "${url}",
"logo": "${logo}",
"foundingDate": "${foundingDate}",
"founders": [{
"@type": "Person",
"name": "${legalName}"
}],
"contactPoint": [{
"@type": "ContactPoint",
"email": "${contact.email}",
"contactType": "customer service"
}],
"address": {
"@type": "PostalAddress",
"addressLocality": "${address.city}",
"addressCountry": "${address.country}"
},
"sameAs": [
"${socialLinks.instagram}",
"${socialLinks.twitter}",
]
}`;
To inject it, Gatsby provides us with a React Helmet that can be edited. We have to pass the data in the form of an application/ld+json
script.
const SEO = ({ description, lang, meta, title }) => {
const { site } = useStaticQuery(graphql`
{
site {
siteMetadata {
title
description
author
}
}
}
`);
const metaDescription = description || site.siteMetadata.description;
return (
<Helmet>
<script type='application/ld+json'>
{structuredDataOrganization}
</script>
</Helmet>
);
};
Changing the media query breakpoints
It's very simple to override themes with Base Web. Their custom breakpoints example was all I needed.
import { LightTheme } from 'baseui';
// Specify your custom breakpoint sizes here
const breakpoints = Object.freeze({
small: 769,
medium: 1024,
large: 1216
});
const ResponsiveTheme = Object.keys(breakpoints).reduce(
(acc, key) => {
acc.mediaQuery[
key
] = `@media screen and (min-width: ${breakpoints[key]}px)`;
return acc;
},
{
breakpoints,
mediaQuery: {}
}
);
export default { ...LightTheme, ...ResponsiveTheme };
🌯 It's a wrap!
Creating this project was a great way to learn Gatsby and how it works under the hood. It's a Framework that gets comfortable very quickly and allows you to do and focus on creating your web apps. It does this by giving you the tools you need, when you need them, and comes with amazing built-in configurations that are production-ready.
In terms of Base Web, it's a great design system to build apps and prototypes with, and can easily be overridable. I especially like that it does not have many components that commonly bloat web app - It has the right ones you (and I) probably need.
Share your thoughts!
What is your experience with Gatsby? Let me know - I'd love to learn what you have to show and tell!.
Photo by Soragrit Wongsa on Unsplash.
Top comments (6)
I see that you're using the Gatsby connecter to get Instagram posts. Keep in mind that by the end of the month, the Instagram API will be shut down. You'll always have to use the Facebook Graph API starting that date
Thanks for the heads up Arnaud! I was not aware of this - Seems like I'll be having to migrate to the Instagram Basic Display API.
I'm curious if your Gatsby-driven Instagram page can stay up to date with the latest 12 posts automatically. Do you have to manually do something to update it when you make a new post?
Hi Paul, that's a great question!
While I have not had the need to check such a case, I can assume possible scenarios that might cause these delays:
All the best!
This was really interesting... Great post!
Thank you! Glad you liked it.