Guide · Next.js

Add an Instagram feed to Next.js

Two good ways to put a live Instagram feed in a Next.js app: drop in the Simpleembed widget as a small client component, or fetch the public JSON API in a Server Component and render the posts with your own markup. Both auto-update as you post to Instagram.

1Get your feed ID

Create a free Simpleembed account, connect your Instagram Business or Creator account, and create a feed. On the Feeds page, open the Embed code dialog — the data-feed-idvalue in the snippet is your feed ID. You'll use it in both approaches below.

2Simplest: install the npm package

simpleembed-react ships the auto-resizing widget component and a typed data hook — TypeScript types included, App Router ready:

npm install simpleembed-react
import { InstagramFeed } from "simpleembed-react";

<InstagramFeed feedId="YOUR_FEED_ID" />

Prefer no extra dependency? Both manual options below do the same thing with copy-paste code.

3Manual option A: the widget as a React component

The standard embed snippet doesn't work as-is in JSX — React never executes <script> tags it renders. Inject the script in an effect instead; the widget handles layout, lightbox, and auto-resizing exactly like the plain-HTML embed:

"use client"; // Next.js App Router only — plain React apps can drop this line

import { useEffect, useRef } from "react";

export function InstagramFeed({ feedId }: { feedId: string }) {
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const container = ref.current;
    if (!container) return;

    // Script tags in JSX don't execute — inject it imperatively instead.
    const script = document.createElement("script");
    script.src = "https://simpleembed.com/embed.js";
    script.async = true;
    script.dataset.feedId = feedId;
    container.appendChild(script);

    return () => {
      container.replaceChildren(); // remove iframe + script on unmount
    };
  }, [feedId]);

  return <div ref={ref} />;
}

Then anywhere in your app:

<InstagramFeed feedId="YOUR_FEED_ID" />

4Manual option B: a Server Component on the JSON API

For full control over markup and styling, skip the widget and fetch the feed's public JSON endpoint on the server. The response shape:

type FeedResponse = {
  feed: { id: string; name: string; source: string };
  posts: Array<{
    id: string;
    mediaType: "IMAGE" | "VIDEO" | "CAROUSEL_ALBUM";
    caption: string | null;
    permalink: string;
    imageUrl: string;
    timestamp: string;
    childCount: number;
  }>;
};

Fetch it with incremental revalidation so new posts appear without a redeploy:

// app/components/instagram-grid.tsx — Next.js Server Component

export async function InstagramGrid({ feedId }: { feedId: string }) {
  const res = await fetch(`https://simpleembed.com/api/feeds/${feedId}`, {
    next: { revalidate: 300 }, // the API is CDN-cached ~5 min anyway
  });

  if (!res.ok) return null;
  const { posts }: FeedResponse = await res.json();

  return (
    <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 8 }}>
      {posts.map((post) => (
        <a key={post.id} href={post.permalink} target="_blank" rel="noreferrer">
          <img
            src={post.imageUrl}
            alt={post.caption ?? "Instagram post"}
            loading="lazy"
            style={{ width: "100%", aspectRatio: "1", objectFit: "cover" }}
          />
        </a>
      ))}
    </div>
  );
}

Always render images through the imageUrlthe API gives you — it proxies Instagram's CDN so images never expire. For an iframe-only variant and a client-side hook, see the full React documentation.

Customize it

If you use the widget, layout, post count, and styling all live in your Simpleembed dashboard — change a setting there and every embedded feed updates in place, with no code changes or redeploys. The JSON API route is the opposite trade: you own the markup, so customization happens in your components.

Frequently asked questions

Should I use the widget component or the JSON API?
Use the widget if you want zero design work — it ships a responsive grid, lightbox, and auto-resizing out of the box. Use the JSON API when the feed has to match your design system exactly; you get raw post data and build the markup yourself.
Does this work with the Pages Router?
Yes. The widget component is plain React and works in either router (drop the "use client" directive outside the App Router). The Server Component example is App Router-only — in the Pages Router, fetch the JSON API in getStaticProps or getServerSideProps instead.
How quickly do new Instagram posts show up?
Simpleembed syncs your Instagram account automatically and the API response is CDN-cached for about five minutes. The widget always shows the latest sync; the Server Component picks up new posts on the next revalidation, so with revalidate: 300 you are at most a few minutes behind — no redeploy needed.
Can I use next/image with the API's imageUrl?
Yes. imageUrl points at Simpleembed's image proxy (so images never expire, unlike hotlinked Instagram CDN URLs). Allow that host under images.remotePatterns in next.config.ts and pass imageUrl straight to next/image.
How much does Simpleembed cost?
One Instagram source and one feed are free forever, including API access. The $7/month plan unlocks unlimited feeds and sources.

Get your feed ID in about two minutes

Create a free Simpleembed account, connect your Instagram Business or Creator account, and copy your embed snippet. One source and one feed are free forever; $7/month unlocks unlimited feeds, sources, and views.

Start free