Cover image - Build a free link in bio page for Instagram with this tutorial

Build your own, free link in bio page for Instagram

Reading time: About 6 min.

Published on Monday, September 30, 2019
Last edited on Friday, October 04, 2019

Instagram does an amazing job at getting users engaged in their app, and then keeping them there. One way they achieve this is by not allowing clickable hyperlinks to external websites to be included in posts. Links can still be included in posts, but rely on users opening a browser window on their phone, and manually typing in the address. That's not going to happen.

Sure, if you have more than 10,000 followers, you can put them in your stories. However, if you want to use individual Instagram posts to direct users to content, then your only option is to put a link in your bio. And let's face it, no one's going to change their bio link every time they make a new post. As a result, a few services have sprung up to provide a better bio link experiences for users. In this post, we're going to build our own using Eleventy, Airtable, and Netlify.

Before we start, though, let me list some of the pros and cons of the approach we're going to be building.

Pros:

  • Can completely customise the look of your page with branding, logos, and additional links.
  • Can be built as an extension to your current website if you so wish.
  • An entirely server-side solution, so there are minimal security risks.
  • Provides users with a more pleasant "link in bio" experience.

Cons

  • The solution we will build is intended for small-scale use.
  • There is a degree of manual input required for updating content & triggering a new build of the site.

You can see a working version of this page at my very own Instagram link in bio page. Of course, it would also be great if you follow me on Instagram as well!

Let's get started.

Setup

Eleventy

Let's first setup Eleventy. We'll create a project folder, and install Eleventy locally there.

mkdir link-in-bio
cd link-in-bio
npm install @11ty/eleventy --save-dev

With that done, let's create a simple folder structure and our Eleventy config file. Be sure to create/save the config file in the root directory for your project.

root
└───src
    ├───_data
    ├───_includes
        └───css
    └───utils
.eleventy.jsmodule.exports = function(eleventyConfig) {  
    return {
        dir: {
            input: "src",
            output: "dist"
          }
    };
  };

Airtable

If you're not familiar with Airtable, it is a spreadsheet-database hybrid that allows for real-time online collaboration. We'll be using it to store the data for our links, and retrieving that information using their API.

To get started, you'll need to sign up to Airtable ( referral link - I'll get a few Airtable credits if you use it :)). Once you're done, create a new base (what Airtable calls 'spreadsheets'). You want to create a table called links with the following columns:

  • published- This will be used to sort our links & show them in date order.
  • linkTo- This is the link you want to direct users to.
  • igLink- This is the link of your Instagram post.

With that done, we're now ready to start building.

Build

Getting data with the Airtable API

We need to bring data into Eleventy, and then have it available for use in our templates and pages later on. To do that, we'll create a file in our _datafolder that will pull in data from the Airtable API.

_data/links.jsconst getLinks = require("../utils/getLinks");

module.exports = async function() {
    return await getLinks()
}

Before we create the getLinks.js file in our utils folder, we'll install Airtable's official Node.js client. Flavio Copes has an excellent post about using the client, which I would definitely recommend checking out.

With the client installed, let's go on and write the code to bring the data from Airtable back into our project.

utits/getLinks.js//Brings in the node.js client
const Airtable = require("airtable");

//Use our Airtable API key to configure the client
Airtable.configure({
  apiKey: process.env.AIRTABLE_API_KEY
});

//Configure the Airtable base and table that we'll be taking data from
const base = Airtable.base("appy7Z6YjUezyw8yp");
const table = base("links");


function generateLinks(link) {
  return {
    ...link
  };
}

/*
Create a promise to get the linkTo and igLink fields from the table, sorted by published date.
The records retrieved are added to an array and passed back to our data file.
*/
module.exports = async function getLinks() {
  return new Promise((resolve, reject) => {
    
    var links = [];
    
    table
      .select({ 
        view: "Grid view",
        fields: ["linkTo", "igLink"],
        sort: [{field: "published", direction: "desc"}] }).eachPage(
      function page(records, fetchNextPage) {
        records.forEach(function(element, index) {
          links.push(element.fields);
        });

        fetchNextPage();

        const prepLinks = links.map(generateLinks);
        resolve(prepLinks);
      },
      function done(err) {
        if (err) {
          console.error(err);
          reject(err);
        }
      }
    );
  });
};

You can find the API key, and base ID to use in the code above at Airtable's API portal (https://airtable.com/api). As an aside, I really like how Airtable's API documentation is presented. It's clear, provides handy examples, and is easy to follow.

You'll also notice we've used environment variables to ensure our API key is secret. If you're unsure how to set these up, take a look at this handy code note.

Now that we can bring in data from our Airtable base, we can create a simple page to display it. I'll be using Liquid as my templating language here, but you can easily substitute it for another which Eleventy supports.

index.html<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Instagram Links Page</title>
		<link rel="stylesheet" type="text/css" href="/_includes/css/styles.css" />
</head>

<body>
    <div class="container">
        <h1>Instagram Links for My Business</h1>
        <p>Thanks for following me on Instagram. If you've reached this page looking for a link that I've mentioned in a post, you can get there by clicking the related image below.</p>
        <div class="holder">
            {% for l in links %}
            <div class="link-item">
                <a href="{{ l.linkTo }}">
                    <img src="{{ l.igLink }}media/?size=l">
                </a>
            </div>
            {% endfor %}
        </div>
    </div>
</body>

</html>

A note about the code above:

  • <img src="{{ l.igLink }}media/?size=l" class="img-responsive lazy"> This bit of code is what gets the image from our Instagram feed, and turns it into an image link we can use on our page. To get the Instagram image of the related post without relying on Facebook's Instagram API, we are using a little hack BadriRavi posted on Stackoverflow. For this to work, you need to ensure the Instagram account you're sharing images from is public.

We can now just add a bit of styling to the page, and we're just about done.

/_includes/css/styles.cssbody,
html {
  margin: 0;
  padding: 0;
  border: 0;
  width: 100vw;
  font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
  font-size: 18px;
}

body {
  background: -webkit-gradient(linear, left bottom, left top, from(#b0cfff), to(#434c7d));
  background: linear-gradient(0deg, #b0cfff, #434c7d);
}

.container {
  width: 100%;
  height: 100%;
  max-width: 1280px;
  padding: 0 0.5rem;
  margin: 0 auto;
  -webkit-box-sizing: border-box;
          box-sizing: border-box;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-orient: vertical;
  -webkit-box-direction: normal;
      -ms-flex-direction: column;
          flex-direction: column;
  -webkit-box-pack: center;
      -ms-flex-pack: center;
          justify-content: center;
}

.container {
  text-align: center;
  color: #fff;
}

.holder {
  width: 100%;
  padding: 1.2rem;
}

.link-item img {
  height: 100%;
  width: 100%;
  -o-object-fit: contain;
     object-fit: contain;
}

.link-item {
  width: 320px;
  height: 320px;
  margin: 1rem auto;
  height: -webkit-fit-content;
  height: -moz-fit-content;
  height: fit-content;
  border-radius: 5px;
  position: relative;
  -webkit-box-sizing: border-box;
          box-sizing: border-box;
  overflow: hidden;
}

@media screen and (min-width: 640px) {
  .holder {
    display: -ms-grid;
    display: grid;
    grid-gap: 1rem;
    -ms-grid-columns: (380px)[auto-fit];
        grid-template-columns: repeat(auto-fit, 380px);
    -webkit-box-pack: center;
        -ms-flex-pack: center;
            justify-content: center;
  }
}

For completeness, we'll need to pass through the styles.css file we've created here so that it is available in Eleventy's build folder.

.eleventy.jsmodule.exports = function(eleventyConfig) {  
  eleventyConfig.addPassthroughCopy("./src/_includes/css/*.css");

    return {
        dir: {
            input: "src",
            output: "dist"
          },
        addPassthroughCopy: true
    };
  };

To check everything works, you can add a few items to the Airtable base. Here are a few I have from my Instagram business account.

Snapshot of sample data from Airtable
Snapshot of sample data from Airtable

Deploy

For this page to be useful, we'll be using webhooks to rebuild our site and publish it on Netlify each time a new link is added. There's a couple of ways to set this up. Probably the most common way to achieve this is using Zapier to connect Airtable to Netlify. However, Zapier only polls Airtable bases every 15 minutes for new records. This would mean there'll be a delay between your post being published and your site being updated. Instead, I'll opt to use IFTTT, which allows me to set up a button on my mobile that builds my site whenever it is pressed. Obviously, this approach is suitable for small scale operations where you have control over when posts are made. Here's the setup I have for my own Instagram links page.

Create a Netlify build hook

After you've deployed your site to Netlify:

  1. Go into your Netlify site's Build & Deploy settings
  2. Select Continuous Deployment
  3. Scroll down and add a Build hook
  4. Give it any name you'd like
  5. Select the git branch you'd like to publish with the webhook. Usually, it's safe just to keep it set to master
  6. Save it, and you will be provided you with a long URL https://api.netlify.com/build_hooks/hook_ID. You'll need this URL in IFTTT.

Create an IFTTT applet

  1. Sign up/login to IFTTT
  2. Go to Explore, then click Make your own applet from scratch. Or just click this link.
  3. Click +this
  4. Select the Button widget service
  5. Select the When button is pressed option
  6. Click +that
  7. Select the Webhooks service
  8. Select the Make a web request option
  9. For the URL field, use the Build Hook URL you've already retrieved from Netlify.
  10. For the Method field, choose POST.
  11. For the Content Type field, choose application/x-www-form-urlencoded.
  12. For the Body field, type {}.
  13. Click the Create action button.
  14. Click Finish.

Now every time you update your Airtable base with new Instagram links, you can simply press the IFTTT button, and a new build of your site will instantly start on Netlify.

Some caveats & extras

  • This approach is somewhat manual in that you need to update the Airtable base yourself each time you've got a post you want to share.
  • You also need to trigger a new build of your site as we've done using IFTTT just above. As I've mentioned, you could you Zapier. However, there might be a slight delay until your new Airtable record is detected.
  • To make updating the Airtable base a little easier, you might want to look at creating a form for that base within Airtable. More information here.

If you need a hand going through this tutorial, you can find the source code and final demo page linked below.

Source code | Demo

Need a hand, or want to leave a comment?
Get in touch by email or on Facebook Messenger.