šŸ—“ļø Everything *[NYC] is back. A free gathering for AI builders. Sept 9 →

Instagram Embed

Embed Instagram posts, reels and IGTV videos anywhere inside an article. Editors paste either a post URL or Instagram's full embed code — both are reduced to a clean permalink, so no tracking parameters or markup end up in your content. A live preview inside the editor shows what was picked before publishing. Ships a separate `/render` entry point with a React component and a @portabletext/react mapping, so the frontend renders posts without importing `sanity` into the app bundle.

Install command

npm i sanity-plugin-sgntech-instagram-embed

sanity-plugin-sgntech-instagram-embed — embed Instagram posts anywhere in your article

sanity-plugin-sgntech-instagram-embed

Embed Instagram posts, reels and IGTV videos anywhere inside a Sanity Studio article — plus a React renderer for your frontend.

npm version license


What you get

  • An instagramPost block for Portable Text — editors drop it between paragraphs, wherever the post belongs.
  • Paste either a post URL or Instagram's full <blockquote> embed code. Both are reduced to a clean permalink, so no tracking parameters or markup end up in your content.
  • Posts, reels (/reel/, /reels/) and IGTV (/tv/) links are supported, including links that carry a profile segment.
  • A live preview of the post inside the editor, so you can see what you picked before publishing.
  • sanity-plugin-sgntech-instagram-embed/render — a React component and a @portabletext/react mapping for the frontend. That entry point does not import sanity, so your app bundle stays clean.

Installation

npm install sanity-plugin-sgntech-instagram-embed

Requires Sanity Studio v5 or v6 and React 18 or 19.

šŸ“¦ On npm: sanity-plugin-sgntech-instagram-embed

Usage in the Studio

Add the plugin and register the block in the Portable Text field it should be available in:

// sanity.config.ts
import {defineArrayMember, defineConfig, defineField} from 'sanity'
import {instagramEmbed} from 'sanity-plugin-sgntech-instagram-embed'

export default defineConfig({
  // ...
  plugins: [instagramEmbed()],

  schema: {
    types: [
      {
        type: 'document',
        name: 'article',
        fields: [
          defineField({
            type: 'array',
            name: 'body',
            of: [
              defineArrayMember({type: 'block'}),
              defineArrayMember({type: 'instagramPost'}), // ← the embed block
            ],
          }),
        ],
      },
    ],
  },
})

Editors then pick Instagram post from the "Add item" menu of the editor, paste a link or an embed code, and optionally switch the caption on.

Options

instagramEmbed({
  name: 'instagramPost', // schema type name
  title: 'Instagram post', // label in the editor's insert menu
  captionedByDefault: false, // initial value of the "Show caption" field
  showEmbedPreview: true, // render the post while the block is open in the editor
})

Stored value

{
  "_type": "instagramPost",
  "_key": "a1b2c3",
  "url": "https://www.instagram.com/p/DbdPtqcDQs3/",
  "captioned": true
}

Only the canonical permalink is stored. Everything else — author, image, caption — is fetched by Instagram at render time, which is what their embed terms require.

Rendering in your frontend

With @portabletext/react

import {PortableText} from '@portabletext/react'
import {instagramPortableTextComponents} from 'sanity-plugin-sgntech-instagram-embed/render'

export function ArticleBody({body}) {
  return <PortableText value={body} components={instagramPortableTextComponents} />
}

Merge it with your own components when you have more block types:

const components = {
  types: {
    ...instagramPortableTextComponents.types,
    image: MyImageBlock,
  },
}

Custom type name or fixed options:

import {createInstagramPortableTextComponents} from 'sanity-plugin-sgntech-instagram-embed/render'

const components = createInstagramPortableTextComponents({
  typeName: 'socialEmbed',
  className: 'my-embed',
  maxWidth: 480,
})

Standalone component

import {InstagramEmbed} from 'sanity-plugin-sgntech-instagram-embed/render'
;<InstagramEmbed url="https://www.instagram.com/p/DbdPtqcDQs3/" captioned maxWidth={540} />

The component renders the same markup Instagram's own embed code produces and loads https://www.instagram.com/embed.js once per page. It is safe to server-render: the script is only touched inside effects, and before it loads — or if a content blocker drops it — the block stays a plain link to the post.

Helpers

import {isInstagramInput, parseInstagramInput} from 'sanity-plugin-sgntech-instagram-embed/render'

parseInstagramInput('https://www.instagram.com/reels/Cx1y2z3AbC/?utm_source=ig_web_copy_link')
// => {id: 'Cx1y2z3AbC', kind: 'reel', permalink: 'https://www.instagram.com/reel/Cx1y2z3AbC/'}

Notes

  • Content Security Policy: the embed loads scripts and frames from Instagram. If your site sets a CSP, allow script-src https://www.instagram.com and frame-src https://www.instagram.com.
  • No Subresource Integrity: embed.js is an unversioned, mutable file on Instagram's servers, so an integrity hash would break the embed on their next deploy. This is the same trade-off every official Instagram embed makes.
  • Private or deleted posts cannot be embedded — Instagram renders a placeholder instead.
  • This project is not affiliated with, endorsed by, or sponsored by Instagram, Meta or Sanity.

Develop

npm install
npm test            # parser unit tests
npm run lint
npm run build
npm run link-watch  # build + publish to a local yalc repo for Studio testing

Built with @sanity/plugin-kit. See Testing a plugin in Sanity Studio for the local development loop.

License

MIT Ā© SGNTech

Related contributions