Instagram feed

The Instagram module puts the latest posts of your own Instagram account on a page, as a grid that opens each picture in a viewer or links through to Instagram. By the end of this chapter your site will have a feed block showing your account's posts, kept up to date by itself.

It is a module and not a feature (Modules): a site that does not list it has no tables and no scheduled job for it. It uses only Meta's official Instagram API. Nothing is scraped and no third-party feed service is involved.

What it can show

It shows the posts of one Instagram professional account, which is a Business or Creator account: the account you connect. A personal account cannot be shown, because Meta's official API does not serve one. Switching is free and quick. In the Instagram app, open Settings, find “Account type and tools”, and choose “Switch to professional account”.

Other people's accounts by username, and hashtags, are not offered. Both need Facebook Login and a review by Meta, and hashtags also need business verification.

Install the module

  1. In your site's folder, install the package:
pnpm add @jamiemcconnell/cms-instagram
  1. List it under modules in site.config.ts:
site.config.ts
modules: ['@jamiemcconnell/cms-instagram'],
  1. Create the module's tables:
pnpm cms migrate
  1. Restart the site. In development, stop pnpm dev with Ctrl+C and run it again. An Instagram entry now appears in the admin's sidebar, for admins.

Create the Meta app

Instagram only lets a website read an account's posts through an app registered with Meta, the company behind Instagram and Facebook. Each site brings its own app. That keeps its keys and limits separate, and it needs no review from Meta. You do this once.

Meta renames its menus now and then, so if a label below differs a little, look for the nearest match.

  1. Sign in at developers.facebook.com with a Facebook account, and register as a Meta developer if it asks you to.
  2. Choose Create App. Give it a name, pick the use case “Manage messaging and content on Instagram”, and make it a Business type app.
  3. In the App Dashboard, open Instagram and choose “API setup with Instagram business login”.
  4. Go to the step called “Set up Instagram business login” and open “Business login settings”.
  5. Copy the Instagram app ID and the Instagram app secret shown there. Do not use the Facebook App ID and App secret under App settings → Basic; those will not work.
  6. Under OAuth redirect URIs, add your site's address followed by /api/admin/modules/instagram/callback, such as https://example.com/api/admin/modules/instagram/callback, and save. It must be https and match CMS_SITE_URL exactly. The Instagram screen in the admin shows the exact value to copy. Meta's dashboard may add a trailing slash when it saves; if it does, use the form it stored.
  7. Add your Instagram account as a tester. In the App Dashboard open App roles → Roles, choose Add People, pick Instagram Tester and enter the account's username. Then, signed in to that Instagram account on instagram.com, open Settings → Website permissions (shown as “Apps and websites” in some versions), then Tester Invites, and accept the invite. It can take a few minutes to appear.
  8. Leave the app in Development mode. That is enough for a site's own account, because accounts with a role on the app need no review.

Give the site the keys

Put the two values from step 5 in the site's .env. On a server that runs Docker, compose.yaml passes .env through to the site, so no other change is needed. Restart the site afterwards; on a server, docker compose up -d does it.

.env
CMS_INSTAGRAM_APP_ID=your-instagram-app-id
CMS_INSTAGRAM_APP_SECRET=your-instagram-app-secret

Connect your account

  1. In the admin, open Instagram and choose Connect Instagram.
  2. Sign in to Instagram and approve. You land back in the admin.

The site copies the latest 24 posts into its own storage, because Instagram's image links expire. After that it syncs every hour and renews its access token by itself. To sync now, choose Sync now in the admin, or run this in your site's folder:

pnpm cms jobs run instagram:sync

On a server that runs Docker, run that as docker compose run --rm app pnpm cms jobs run instagram:sync. Disconnect, on the same screen, removes the stored token and every copied image.

Put the feed on a page

Open a page in the editor and add the Instagram feed block, from the Elements group of the block picker. It has these settings:

  • Posts to show, from 1 to 24 (9 by default).
  • Columns on larger screens and Columns on phones (3 and 2 by default), with the Gap between posts and the Corners.
  • Thumbnail shape: Square (the default), Portrait (4:5) or Original.
  • Captions: None (the default), On hover or Below each post.
  • Clicking a post: Opens a viewer (the default), or Opens Instagram in a new tab.
  • Follow button label, for a button to your profile. Leave it empty for no button.

Public pages are cached, so a new post can take up to five minutes after a sync to appear. Until an account is connected and has synced, editors see a placeholder note in the block and visitors see nothing.

When something goes wrong

  • “Invalid redirect_uri”. The address does not match what Meta saved. Copy it from the Instagram screen in the admin, check that it is https and matches CMS_SITE_URL, and mind a trailing slash that the dashboard may have added.
  • “Insufficient developer role”, or the account is not offered. The account is not an accepted tester of the app, or it is not a professional account. Add it under App roles → Roles, accept the invite on Instagram, and switch the account type if needed.
  • The admin says keys are missing. The environment was not loaded. Check the names in .env and restart the site.
  • “Not connected” after it worked. Changing CMS_AUTH_SECRET, or an outage long enough for the token to expire, leaves a token that cannot be read or renewed. Choose Connect Instagram again.

Run it on a server lists the environment variables, and Troubleshooting covers problems that are not about Instagram.