Open Graph images after leaving Framer

What happens to a Framer site's Open Graph and social images when you move it to code, how to fix broken link previews, and how to generate images per page.

Published
Reading time
5 min
Deframer cover: “Link previews that still work.” Beside it, a list of og:image, metadataBase and Share preview with Share preview selected
On this page8 sections

When you convert a Framer site with Deframer, every page keeps the social image it had: the URL Framer published as its og:image is written into that page's metadata, and CMS detail pages keep one per item. Link previews break after a move for three usual reasons: metadataBase still points at the old domain, images were moved into the project and are now relative paths, or the platforms' preview caches still hold the old card. Fix the domain, check the tags with curl, then refresh the caches. If you want new images per page, Next.js can generate them from code.

This guide covers where the images are in the project, how to check them, how to replace one, and how to generate them. Deframer is an independent tool and is not affiliated with, endorsed or sponsored by Framer.

What Framer publishes for social sharing

In Framer you set a social image for the whole site and, optionally, for each page; CMS detail pages can take theirs from a field of the item. On publish, Framer writes them into each page's head as Open Graph and Twitter card tags:

html
<meta property="og:image" content="https://framerusercontent.com/images/….png">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://framerusercontent.com/images/….png">

Platforms such as LinkedIn, Slack, X, iMessage and WhatsApp read these tags to build the preview card.

Where the images end up after converting

Deframer evaluates each page's metadata when it converts the site and writes the result into the route files, so the images come along with the title, description and canonical URL:

Framework

Where a page's social image is set

Next.js

The metadata export of src/app/**/page.tsx; CMS items in the route's metadata.json

TanStack Start

The route's head() in src/routes/**/index.tsx, or its head.json

Astro

The page's index.astro head; CMS items in _head.json

React Router

The route module's meta; CMS items in head.json

Vite

The route's <PageHead>; CMS items in head.json

The image URLs themselves depend on the assets option you chose:

  • Assets on Framer's CDN (the default): the URL stays an absolute framerusercontent.com address. It keeps working as long as Framer serves the file.
  • Assets in the project: the image is downloaded into public/ and the metadata uses a path on your own site. In Next.js, the root layout's metadataBase turns that path into an absolute URL.

1. metadataBase points at the old domain

The root layout in a Next.js project sets metadataBase to the site's published URL. That's correct if the domain doesn't change. If it does, relative image paths and canonical URLs resolve to the old address. Change it in src/app/layout.tsx:

ts
export const metadata: Metadata = {
  metadataBase: new URL("https://www.example.com"),
  // …
};

The SEO migration guide explains why this matters for canonical tags too.

2. Relative paths on a framework that doesn't resolve them

Open Graph requires an absolute URL. Next.js resolves relative paths with metadataBase; in the other frameworks, check that the image URLs in the head tables are absolute and use your production domain.

3. Cached previews

Platforms cache cards, sometimes for days. A preview showing the old image after you fixed the tags is usually a cache. Most platforms offer a way to refresh: LinkedIn's Post Inspector re-scrapes a URL, and Facebook's Sharing Debugger has a "Scrape Again" button. For others, adding a harmless query string such as ?v=2 to the shared link forces a fresh fetch.

4. The image isn't reachable

If Framer's CDN copy is gone (for example after you delete the asset in Framer and republish), the URL breaks. Switching to project assets avoids that dependency: see CDN vs local assets.

How to check a page's social tags

Run this against a preview or production URL:

sh
curl -s https://www.example.com/blog/my-post | grep -Eo '<meta (property|name)="(og|twitter):[^"]*" content="[^"]*"'

You should see og:title, og:description, og:image and the Twitter equivalents, with absolute https:// image URLs on the right domain. Then fetch the image itself:

sh
curl -sI "https://www.example.com/images/social.png" | head -1

A 200 with an image content type is what you want. Platforms commonly recommend a 1200 × 630 pixel image, under a few megabytes.

How to replace a page's image

For a static page in Next.js, edit the route's metadata:

ts
export const metadata: Metadata = {
  title: "Pricing",
  openGraph: { images: [{ url: "/social/pricing.png", width: 1200, height: 630 }] },
  twitter: { card: "summary_large_image", images: ["/social/pricing.png"] },
};

Put the file in public/social/pricing.png. For a CMS detail page, edit the item's entry in the route's metadata.json, or point it at the item's image field if you'd rather derive it.

How to generate images per page

Hand-made images don't scale to a blog with hundreds of posts. Next.js supports an opengraph-image.tsx file in a route folder that renders the image from JSX at build time, with ImageResponse from next/og:

tsx
// src/app/blog/[slug]/opengraph-image.tsx
import { ImageResponse } from "next/og";
import posts from "@/cms/blog.json";

export const size = { width: 1200, height: 630 };
export const contentType = "image/png";

export function generateStaticParams() {
  return posts.map((post) => ({ slug: post.slug }));
}

export default async function Image({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params;
  const post = posts.find((item) => item.slug === slug);
  return new ImageResponse(
    <div style={{ width: "100%", height: "100%", display: "flex", alignItems: "flex-end", padding: 64, background: "#0f1014", color: "white", fontSize: 64 }}>
      {post?.title}
    </div>,
    size,
  );
}

A file-based image takes precedence over an image set in the route's metadata, so remove the explicit openGraph.images for that route or the two compete. The API details change between Next.js versions; check the documentation in node_modules/next/dist/docs/ for the version in your project.

In the other frameworks, the simplest path is a build script that renders images with a library such as Satori and writes them to public/social/, then points each route's head at its file. Deframer's own blog covers are drawn that way.

A checklist before launch

  1. metadataBase (Next.js) or the absolute image URLs (other frameworks) use the production domain.
  2. Every important page returns og:image with an absolute URL.
  3. The image URLs return 200.
  4. CMS detail pages each have their own image, not the site default, if they did on Framer.
  5. You've re-scraped the home page and a few key pages in LinkedIn's and Facebook's tools after launch.

This sits naturally in the wider Framer to Next.js migration checklist, and pairs with structured data for how pages look to machines rather than people.

FAQ

Do my Framer social images survive the conversion?

Yes. Each page's title, description and social image are evaluated from the published site and written into the route's metadata, and CMS detail pages keep a per-item table.

Why does LinkedIn still show my old preview?

LinkedIn caches previews. Check the tags with curl first; if they're right, use LinkedIn's Post Inspector to re-scrape the URL.

Should social images stay on Framer's CDN?

They work there, which is why it's the default. If you're cancelling your Framer plan or want no dependency on Framer, convert with "Assets in the project" so the images are files you host.

What size should an Open Graph image be?

1200 × 630 pixels is the common recommendation across platforms, in PNG or JPEG. Keep important text away from the edges, since some platforms crop.

Can I generate a social image for every blog post automatically?

Yes. In Next.js, an opengraph-image.tsx file in the post route renders one per post from its CMS record at build time. Other frameworks can do the same with a build script.