Opens an external site in a new window
Mental Health Awareness Month
“Community”
RODNEY LAB
  • Home
  • Plus +
  • Newsletter
  • Links
  • Profile
RODNEY LAB
  • Home
  • Plus +
  • Newsletter
  • Links

SvelteKit Infinite Scroll: Instagram API Tutorial # SvelteKit Infinite Scroll: Instagram API Tutorial #

blurry low resolution placeholder image Use Vim Keyboard Shortcuts on your Blog
  1. Home Rodney Lab Home
  2. Blog Posts Rodney Lab Blog Posts
  3. SvelteKit SvelteKit Blog Posts
<PREVIOUS POST
NEXT POST >
LATEST POST >>

SvelteKit Infinite Scroll: Instagram API Tutorial #

Updated 5 months ago
8 minute read
Gunning Fog Index: 5.9
Content by Rodney
blurry low resolution placeholder image Author Image: Rodney from Rodney Lab
SHARE:

🖱 Infinite Scrolling Feeds in Svelte #

Let’s look at SvelteKit infinite scroll. The Instagram app itself is the perfect example of an infinite scrolling feed. There are potentially many posts available, and the app does not load them all initially; doing so would slow the page down, impacting user experience. Instead, it loads a few posts and as the user scrolls down, it starts lazy loading  more posts. Lazy loading is just a way of saying we load content on demand (or, ideally, when we anticipate demand).

blurry low resolution placeholder image SvelteKit Infinite Scroll: Screenshot: Image shows a website screenshot with six images from an Instagram feed show in a 3-column grid.
SvelteKit Infinite Scroll: Screenshot of feed generated in our app from @askrodney_ Instagram Feed

We will implement infinite scroll on a SvelteKit app, using images from your Instagram feed. In doing so, we need a trigger for automatically loading more content. For this, we can use the Intersection Observer API. When the user scrolls down and the footer becomes visible, we will get an observe event and load more content (where there are more posts available). As well as Intersection Observer, from the Svelte toolkit, we will be using a reactive function and stores.

We focus on an Instagram application for infinite scrolling in this article. However, it is not too much effort to apply the techniques here to a blog roll on your site, feeds from other social sites like Twitter or for user interactions on a social app you are building. If that sounds like something you might find useful, then why don’t we get cracking?

🔑 Instagram Access Token #

We will focus on the SvelteKit side in the post, so that it doesn’t get too long. If you want to code along, you will need an Instagram access token. There are currently two Instagram APIs. Here we just want to get images from a particular user’s feed, and the Instagram Basic Display API matches ours needs. Follow Facebook’s Get Started with Instagram Basic Display API  to get your access token.

blurry low resolution placeholder image SvelteKit Infinite Scroll: Screenshot: Instagram A P I key: image is a screenshot of an authorization screen.  User can select level of authorization and click Allow on Don't Allow.
SvelteKit Infinite Scroll: Instagram Access Token

You will see, as part of the tutorial, you will set up a test user. Use your own Instagram account (or at least the one you want to extract the feed from). Select the Media (optional) box to be able to pull the feed images in, when asked to authorize your account. Once you have an access token, you can move on to setting up the SvelteKit app.

A temporary access token is fine for a proof of concept, though if you want to pursue the product to production you will eventually need longer living tokens.

⚙️ Svelte Setup #

We’ll create a skeleton SvelteKit project and put this thing together from there. To get going, type these commands in the terminal:

    
pnpm dlx sv create sveltekit-infinite-scroll && cd $_
pnpm install
pnpm add @fontsource/playfair-display

Select a skeleton project, answer no to Typescript and yes to both Prettier and ESLint. Let’s create an environment variable file next:

.env
plaintext
    
1 INSTAGRAM_ACCESS_TOKEN=IGQVJ...

Then finally spin up a dev server:

    
pnpm dev

🧱 SvelteKit Infinite Scroll: API Routes #

Next we’ll build a couple of server routes. We will use these to query the Instagram API from the client. First, create src/routes/+page.server. Add the following content:

src/routes/+page.server
javascript
    
1 import { INSTAGRAM_ACCESS_TOKEN } from '$env/static/private';
2 import { error } from '@sveltejs/kit';
3
4 /** @type {import('../../.svelte-kit/types/src/routes/$types').PageLoad} */
5 export async function load() {
6 try {
7 const url = `https://graph.instagram.com/me/media?fields=caption,id,media_type,media_url,timestamp&access_token=${INSTAGRAM_ACCESS_TOKEN}`;
8 const response = await fetch(url, {
9 method: 'GET',
10 });
11
12 const data = await response.json();
13
14 return {
15 data,
16 };
17 } catch (err) {
18 console.log('Error: ', err);
19 error(500, 'Error retrieving data in /api.instagram-feed.json');
20 }
21 }

This code gets called automatically when we load the home page, and we can access the data which we return here from the home page Svelte file. That data is just return the response the server receives from Instagram, (if all is well)! That response will be JSON and something like this:

    
1 {
2 "data": [
3 {
4 "id": "17924392726111111",
5 "media_type": "IMAGE",
6 "media_url": "https://scontent-lhr8-1.cdninstagram.com/v/iamge-url",
7 "timestamp": "2021-10-18T11:09:59+0000"
8 },
9 {
10 "id": "17924392726111112",
11 "media_type": "IMAGE",
12 "media_url": "https://scontent-lhr8-1.cdninstagram.com/v/iamge-url",
13 "timestamp": "2021-10-18T11:09:50+0000"
14 },
15 ],
16 "paging": {
17 "cursors": {
18 "before": "aaa",
19 "after": "bbb"
20 },
21 "next": "https://graph.instagram.com/v12.0/link-for-next-page"
22 }
23 }

There will be up to 25 posts (I just included two here). Note the paging object includes a next link. We will use this when we need to download more images. Let’s code up the endpoint for that next.

Pulling more Images from Instagram API #

To get more images, we just need the next link included in the previous call. Create a server endpoint for pulling more images at src/routes/api/instagram-feed-more.json/+server.js and add this content:

src/routes/api/instagram-feed-more.json/+server.js
javascript
    
1 import { error } from '@sveltejs/kit';
2
3 /** @type {import('./$types').RequestHandler} */
4 export async function POST({ request }) {
5 try {
6 const { next } = await request.json();
7 const response = await fetch(next);
8 const data = await response.json();
9
10 return new Response(JSON.stringify(data));
11 } catch (err) {
12 console.log('Error: ', err);
13 error(500, 'Error retrieving data in /api.instagram-feed-more.json');
14 }
15 }

We will access this endpoint using the POST method and include the next link in the API call body.

With our API routes now all set up, let’s add one more piece of plumbing before we code up the client page.

🛍 Svelte Store #

Initially, we will show six images, though we would have pulled up to 25 in the first API call. The store helps us out here. We put all the images we pulled from Instagram into the store and then (initially) show the first six. As the user scrolls down, we will load more images from the store. Eventually, it’s possible the user will want more images than there are available in the store. At that point, we make a more Instagram call, returning up to 25 more images. We append those new images onto the end of what’s in the store already and we’re away!

That probably sounded more complicated than Svelte actually makes it, but I wanted to run through the logic before we implement it. As it happens, we only need three lines of JavaScript to set this store up in SvelteKit! Create a file at src/lib/shared/store/instagram.js (you will need to create some folders). Add these lines to the file:

src/lib/shared/store/instagram.js
javascript
    
1 import { writable } from 'svelte/store';
2
3 const feed = writable([]);
4
5 export { feed as default };

In line 3, we are initializing the store to an empty array. Let’s add something now from the client.

🧑🏽 Client Side #

As we mentioned earlier, we can access the data we pull from the Instagram API in the src/routes/+page.server.js server route in the Svelte markup We do that next. Replace the content in src/routes/+page.svelte:

src/routes/+page.svelte
svelte
    
1 <script>
2 import instagram from '$lib/shared/stores/instagram';
3 import { onMount } from 'svelte';
4 import { browser } from '$app/environment';
5 import '@fontsource/playfair-display/400.css';
6 import '@fontsource/playfair-display/700.css';
7
8 /** @type {import('./$types').PageData} */
9 let { data } = $props();
10
11 const INITIAL_POSTS = 6;
12
13 const { data: feed, paging } = $derived(data.data);
14 let next = $derived(paging?.next ? paging.next : null);
15 $effect(() => {
16 instagram.set(feed);
17 });
18
19 let limit = $state(INITIAL_POSTS);
20
21 function morePostsAvailable() {
22 return limit < $instagram.length || next;
23 }

We have the feed posts available in the data prop, which we import (Svelte syntax is to use the export keyword here) in line 9. We destructure the feed and then adding the data to the store is simply done in line 16 with instagram.set(feed). Could there be less boilerplate? 😅

I should mention, we imported the store in line 2. In line 22 you see an example of how we can access the store. We just write $instagram and that gives us the array which we set the store to be. In this line, we check how many elements are currently in the store array.

Intersection Observer #

Okay, next we want to be able to show more posts (if we have them) whenever the footer comes into view. The Intersection Observer API is our friend here. If this is your first time using it in Svelte, check out the post on tracking page views, where we look at Intersection Observer in more detail. Add this code to the bottom of src/routes/+page.svelte:

src/routes/+page.svelte
svelte
    
23 let footer;
24
25 onMount(() => {
26 if (browser) {
27 const handleIntersect = (entries, observer) => {
28 entries.forEach((entry) => {
29 if (!morePostsAvailable()) {
30 observer.unobserve(entry.target);
31 }
32 showMorePosts();
33 });
34 };
35 const options = { threshold: 0.5, rootMargin: '-100% 0% 100%' };
36 const observer = new IntersectionObserver(handleIntersect, options);
37 observer.observe(footer);
38 }
39 });
40
41 async function showMorePosts() {
42 try {
43 const newLimit = limit + 6;
44 if (newLimit <= $instagram.length) {
45 // load more images from store
46 limit = newLimit;
47 } else if (next) {
48 // get another page from IG if there is another page available
49 const response = await fetch('/api/instagram-feed-more.json', {
50 method: 'POST',
51 credentials: 'same-origin',
52 headers: {
53 'Content-Type': 'application/json',
54 },
55 body: JSON.stringify({ next: next.replace(/%2C/g, ',') }),
56 });
57 const newData = await response.json();
58 const { data: newFeed, next: newNext } = newData;
59 instagram.set([...$instagram, ...newFeed]);
60 next = newNext ?? null;
61 limit = newLimit;
62 }
63 } catch (error) {
64 console.error('Error fetching more posts in index');
65 }
66 }
67 </script>

We will set the minimum page height so that the footer is initially out of view (in styles which we add in a moment). Our Intersection Observer parameters will observe an intersection event when the user scrolls down and the footer becomes visible. This will call the showMorePosts function. To help here, we will bind the footer variable (in line 25) to the actual rendered footer element.

showMorePosts is declared as a reactive function (in line 41). This is a hint to the Svelte compiler that the function changes some elements in the DOM and a refresh might be needed when it is finished.

In line 56, we just make sure we replace URL encoded commas in the next string with actual commas. Let me know if anything here could do with more explanation, and I can update the post. Let’s actually render the content next.

Client Rendered Markup #

Paste this code at the bottom of src/routes/+page.svelte:

src/routes/+page.svelte
svelte
    
70 <svelte:head>
71 <title>SvelteKit Infinite Feed Scroll</title>
72 <html lang="en-GB" />
73 </svelte:head>
74
75 <header>SvelteKit Infinite Scroll</header>
76
77 <main class="container">
78 <h1>Instagram Feed</h1>
79 <section class="feed">
80 {#each $instagram?.slice(0, limit) as { caption, media_url }, index}
81 <article aria-posinset={index + 1} aria-setsize={$instagram.length} class="feed-image">
82 <img
83 class="lazy"
84 alt={caption ? caption : 'Image from instagram feed'}
85 loading="lazy"
86 decoding="async"
87 width="256"
88 height="256"
89 src={media_url}
90 />
91 </article>
92 {:else}
93 No feed images yet!
94 {/each}
95 </section>
96 </main>
97 <footer bind:this={footer}>
98 <small>Copyright (c) 2021–2022 Rodney Lab. All Rights Reserved.</small>
99 </footer>

There are a few things worth mentioning here:

  • in line 80 we just take the number of posts we want from the store, rather than the whole thing,
  • we use bind:this to attach the footer element to the variable we mentioned before, used above by the Intersection Observer code,
  • I’ve just included the footer content in the example for the sake of the Intersection Observer code.

SvelteKit Infinite Scroll: Styling #

Here’s some (mostly) optional styling, just paste it at the bottom of our file. Be sure at least to set the min-height as in line 127:

src/routes/index.svelte — click to expand code.
src/routes/index.svelte
svelte
    
101 <style>
102 :global(html) {
103 font-family: Playfair Display;
104 background: #e1306c;
105 }
106 :global(body) {
107 margin: 0;
108 }
109
110 :global(:root) {
111 --font-weight-bold: 700;
112 }
113
114 header {
115 color: #ffdc80;
116 max-width: 768rem;
117 padding: 1.5rem;
118 font-size: 3.052rem;
119 font-weight: var(--font-weight-bold);
120 }
121 h1 {
122 color: #ffdc80;
123 font-size: 3.815rem;
124 text-align: center;
125 }
126 .container {
127 min-height: 100vh;
128 }
129
130 .feed {
131 display: grid;
132 grid-template-columns: 1fr 1fr 1fr;
133 grid-template-rows: auto;
134 row-gap: 0;
135 max-width: 768px;
136 margin: 3rem auto;
137 width: 100%;
138 height: auto;
139 }
140
141 .feed img {
142 width: 100%;
143 height: 100%;
144 }
145 .feed-image {
146 width: 100%;
147 height: 100%;
148 }
149
150 footer {
151 background: #833ab4;
152 color: #fff;
153 text-align: center;
154 padding: 1rem;
155 }
156
157 @media (max-width: 768px) {
158 .feed {
159 padding: 0 1.5rem;
160 width: 100%;
161 }
162 }
163 </style>

🗳 Poll #

As a developer, which network do you use most?
Voting reveals latest results.

💯 SvelteKit Infinite Scroll: Testing #

That’s it. Give your browser a refresh and get scrolling! If your internet connection is fast, you might not notice more images loading. Keen an eye on the vertical scroll bar, though, and you will see it jumps as more content (off-screen) loads.

Please enable JavaScript to watch the video 📼

SvelteKit Infinite Scroll: Instagram Test

🙌🏽 SvelteKit Infinite Scroll: What we Learned #

In this post, we learned:

  • using the Instagram API to fetch a user’s posts;
  • how you can use store in Svelte to buffer content received from an external feed; and
  • combining the Intersection Observer API with Svelte stores for a seamless user experience.

I do hope there is at least one thing in this article which you can use in your work or a side project. For extensions, you could add a Twitter or try adapting the code to take Instagram Video posts as well as images. Alternatively, simply use the code to create an infinite feed of your blog posts. The sky is the limit, you can really go to town on this!

As always get in touch with feedback if I have missed a trick somewhere! You can see the full code for this SvelteKit Instagram Infinite Scroll tutorial on the Rodney Lab Git Hub repo .

🙏🏽 SvelteKit Infinite Scroll: Feedback #

Have you found the post useful? Do you have your own methods for solving this problem? Let me know your solution. Would you like to see posts on another topic instead? Get in touch with ideas for new posts. Also, if you like my writing style, get in touch if I can write some posts for your company site on a consultancy basis. Read on to find ways to get in touch, further below. If you want to support posts similar to this one and can spare a few dollars, euros or pounds, please consider supporting me through Buy me a Coffee.

Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via @askRodney on Twitter and also askRodney on Telegram . Also, see further ways to get in touch with Rodney Lab. I post regularly on SvelteKit as well as other topics. Also, subscribe to the newsletter to keep up-to-date with our latest projects.

Thanks for reading this post. I hope you found it valuable. Please get in touch with your feedback and suggestions for posts you would like to see. Read more about me …

blurry low resolution placeholder image Rodney from Rodney Lab
TAGS:
SVELTEKIT

Reposts:

Reposts

  • Javascript all day profile avatar

Likes:

Likes

  • zhuganglie profile avatar
  • Webjeda 🛹 profile avatar
  • Ryan Arpe profile avatar
  • Patrick Gerke profile avatar
Reposts & likes provided by Mastodon & X via Webmentions.
<PREVIOUS POST
NEXT POST >
LATEST POST >>

Leave a comment …

Your information will be handled in line with our Privacy Policy .

Ask for more

1 Nov 2022 — Astro Server-Side Rendering: Edge Search Site
3 Oct 2022 — Svelte eCommerce Site: SvelteKit Snipcart Storefront
1 Sept 2022 — Get Started with SvelteKit Headless WordPress

Copyright © 2020 – 2025 Rodney Johnson. All Rights Reserved. Please read important copyright and intellectual property information.

  • Home
  • Profile
  • Plus +
  • Newsletter
  • Contact
  • Links
  • Terms of Use
  • Privacy Policy
We use cookies  to enhance visitors’ experience. Please click the “Options” button to make your choice.  Learn more here.