A client of mine recently requested a simple image gallery for her site — one which would display her Instagram feed. I thought: “no problemo, Instagram surely has a simple API, and this use-case is probably their most basic Hello World! example.” However, perusing the Instagram API docs, which provide almost no actual code examples, only muddied the waters. Client IDs, client secrets, access tokens? It all seemed like overkill for displaying an individual’s own public Instagram feed. Googling was also of little help — I found mostly dire posts about Instagram shutting down public API access (which is true, but the access we need is supposed to be available till 2020, so: onward!).

However, I came across one example app and saw that the call to Instagram couldn’t be simpler:

let response = await fetch( ‘https://api.instagram.com/v1/users/self/media/recent/?access_token=' + this.access_token )

After getting my own access token and successfully fetching my feed with it, building a simple gallery was surprisingly quick, thanks to Vue.js and CSS Grid.

I hope this article will:

help anyone else who wants to build a simple display of their own Instagram feed

provide a real-world Vue example for Vue beginners

give a taste of the ease and power of CSS Grid for building layouts

Here’s another example of what we’re building: my Instagrams, on my website.

Getting your access token

You can follow the directions at the beginning of this post, or you can use this site to generate your own access token. (NB: With the second option, you are trusting that the company that made the generator tool is not storing or using the generated access token. I did, and nothing terrible has happened yet. All you can do with an access token is browse media, anyway.)

Setting up the page shell

In my case, the gallery was going to sit within a plain vanilla web page, not as part of a web app. This is one thing I really enjoy about Vue: as with jQuery, you can just drop it into your page and use as-needed. It doesn’t have to change the way you build your site or page. Our Instagram gallery is going to be pretty self-contained, aside from two external Javascript resources and, of course, the call to the Instagram API. Here is the shell of the page:

We are bringing in a development version of Vue.js, as well as Axios.js, which is the preferred library for making HTTP requests from the browser in Vue. You can use the native fetch() API, or vue-resource or whatever suits you. If you’re already loading jQuery on the page for other reasons, you can even use its $.ajax() function.

Then we have spots for our styles, a bit of html, and a taste of Javascript.

The Vue code

Let’s be bold and write our Javascript first:

We create a new Vue instance, and first tell Vue what element to control, with el: '#app' . Then we set up some data for Vue to keep track of:



access_token: "your access token here",

url: "

username: "",

grams: [],

next_url: "",

error: false

}, data: {access_token: "your access token here",url: " https://api.instagram.com/v1/users/self/media/recent/ ",username: "",grams: [],next_url: "",error: false},

The url and access_token values won’t change (plug in the access token you retrieved above). The others will be updated later in our code. We also have one computed property:

The value of instapage is just the url of the user’s page on Instagram. Yes, you already know what the username will be for your own gallery, so having that and the instapage as dynamic properties is a little silly, but it demonstrates using computed properties. And, having fewer hard-coded values makes the code a little more generic and easier to reuse in future projects.

Next up we have the heart of our little app, the method getGrams() :

methods: {

getGrams() {

axios.get(this.url + "?access_token=" + this.access_token)

.then(({data}) => {

this.grams = data.data

this.username = data.data[0].user.username

this.next_url = data.pagination.next_url

})

.catch(function (error) {

console.log(error)

this.error = true

});

},

...

}

We use Axios to call the Instagram url with our access_token appended. If successful, we update our data properties. This is the essential shift in thinking you have to make between jQuery and Vue: with Vue, all you worry about is the data. No more finding elements, creating elements, appending elements — the Javascript code you write manipulates and updates the data your app is based on, and you let Vue f*ck with the DOM.

(Side note: somewhat confusingly, Axios returns a response object with a data property, and Instagram’s API also has a data property, which contains an array of our Instagram posts. Hence the nested data properties.)

Now, we need some event to trigger a call to the getGrams() method. Those familiar with jQuery are used to wrapping code in $(document).ready() or one of its equivalents. In our case, we want to call our method not when the document is ready, but when the Vue instance is ready, and for that we can use one of the Vue lifecycle hooks:

created() {

this.getGrams();

}

So once the instance has been ‘created’ by Vue (data observation, computed properties, methods, watch/event callbacks, have all been established), call our getGrams() method, which fetches our Instagram data.

There is a second method on our Vue instance, getMoreGrams() . Along with our array of posts, the Instagram API returns a next_url we can use to fetch the next batch of posts, thusly:

getMoreGrams(){

axios.get(this.next_url)

.then(({data}) => {

this.grams = this.grams.concat(data.data)

this.next_url = data.pagination.next_url

})

.catch(function (error) {

console.log(error)

this.error = true

});

}

Once that new batch of posts comes back, we just add it to our grams array, and Vue takes care of the rest! We’ll provide a button users can click to trigger this method (alternatively you could set up an ‘infinite scrolling’ situation, but I hate those).

For each of these API calls we do some very basic error handling which will display a message if the call failed altogether, by setting the reactive error property to true .

The Vue app body

Next we’ll write our HTML/Vue template code:

Here we have one container div with an id of #app (which can happily sit alongside other HTML elements — your main nav, footer, etc).

After a simple heading with our username, we want the divs containing each link and image to be direct children of our CSS grid. To accomplish that, we use a template element as an invisible wrapper. We can place our v-if conditional here, so that the markup inside will only be output if we have Instagrams to show:

<template v-if="grams.length > 0">

If we do, we loop through each and output an image wrapped in a link, using the data we’ve gotten back from Instagram:

<div v-for="(gram, index) in grams">

<a :href="gram.link">

<img :src="gram.images.standard_resolution.url" :alt="gram.text" />

</a>

</div>

While we don’t have anything to show, we can show a simple loader graphic:

<div v-else class="loading"></div>

And should the call to Instagram fail, we can say we’re sorry:

<div v-if="error" class="error">Sorry, the Instagrams couldn't be fetched.</div>

Lastly, if the viewer just loves our work and can’t get enough, we provide a button to load the next ‘page’ of our feed:

<button @:click="getMoreGrams">Load More</button>

( @ is shorthand for v-on .)

Layout with CSS Grid

Last but not least we can whip together a quick grid layout for our gallery:

First we put a lot of our numerical values in CSS custom properties on the :root element (in our case, the html element). This is handy in three ways:

You can easily ‘re-theme’ the gallery for other sites.

You can keep values consistent easily, without a preprocessor or multiple find/replace.

In dev tools, all the interesting properties to play with are in one place (the html element), without having to jump to declarations on individual elements:

Using CSS custom properties on the root element, values that change the look and feel of the page are all in one place. Neat-o.

Then we set up our grid on #app :

#app{

display: grid;

grid-gap: var(--spacing);

grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));

min-height: calc(100vh - var(--spacing)*2);

}

We give our grid some spacing, and set up columns to have a minimum width of 320px , and a maximum width of 1fr , meaning 1 fraction of the free space available. You can think of fr like the ‘parts’ in a cocktail recipe, for example:

grid-template-columns: 3fr 2fr 1fr;

would be similar to a recipe that calls for 3 parts cognac, 2 parts triple sec, and 1 part fresh lemon juice. It doesn’t tell you anything about the volume of cocktail you’ll make, just what proportion the ingredients should be in relation to each other and the whole.

With repeat and auto-fill we tell the browser to repeat our flexibly-sized column as many times as will fit in the #app div (which in our case is 100% of the browser window). You’ll notice that this makes our grid totally responsive, without a single media query!

The rest of the CSS is fairly straightforward. One nice shorthand used in a few places is:

grid-column: 1 / -1;

This is a simple way to make sure an element spans the entire grid, without knowing how many columns are present, and is shorthand for

grid-column-start: 1;

grid-column-end: -1;

When a negative integer is used, the grid lines will be counted from the end of the grid, so this is saying “start at the first grid line, and end at the last grid line,” regardless of how many grid lines there are.

Wrap-up

See the full code on Gist.

This is a super-quick start on a simple Instagram gallery, that shows off the ease-of-use and power of both Vue.js and CSS Grid.

There are a lot of improvements and additional features you could incorporate here: fancier styling (Polaroid look, anyone?); displaying additional data, such as likes, captions, and tags; more robust error handling, and fallbacks for older browsers.

What improvements would you make? Leave a comment below! If this article helped you in any way, how about a clap or two?

Resources for learning more: