ScreenshotNeo

BlogHow-to

How to create social preview images from Markdown front matter

Turn Markdown front matter into working social preview metadata. Configure the right fields for your framework, then verify the rendered tags and public image URL.

By the ScreenshotNeo team4 October 20269 min read

To create social preview images from Markdown front matter, store each page’s title, description, and image reference in its front matter, then configure your site generator or framework to render those values as metadata in the page’s HTML <head>. Front matter alone does not create social metadata. Verify the built page contains og:title, og:type, og:image, and og:url, and that the image URL is publicly reachable. The field names and path rules depend on your framework.

The examples below cover Quarto, Hugo/Grafana’s documented convention, Next.js, and Jekyll. Use the pattern matching your actual publishing stack; these schemas are not interchangeable.

1. Understand the output you need

Open Graph defines four required basic properties: og:title, og:type, og:image, and og:url. Put them in the document head. A useful og:description can summarize the page for a preview. The Open Graph protocol defines the core properties.

<meta property="og:title" content="A page title">
<meta property="og:type" content="article">
<meta property="og:image" content="https://example.com/images/page-preview.png">
<meta property="og:url" content="https://example.com/articles/example/">
<meta property="og:description" content="A concise page summary.">

The image should represent the specific page. Its URL must identify the actual image asset, rather than a local source-file path that will not exist on the deployed site. Social crawlers consume the rendered page and image URL; they do not generally interpret your source front matter as a substitute for generated metadata.

2. Add page metadata in front matter

Start by adding a title, summary, and image value using the schema your framework expects. A conceptual example looks like this, but do not assume the key image works unchanged in every tool:

---
title: "A page title"
description: "A concise page summary"
image: "/images/page-preview.png"
---

Choose and document one path convention for your project: an absolute public URL, a site-root-relative path, a project-relative path, or a document-relative path. Then confirm how the framework resolves that value during the build. A relative path can be interpreted relative to the source document or to the published site, depending on the tool.

3. Configure the framework to emit metadata

Quarto

Quarto supports Open Graph and Twitter Card output through website configuration. Enable the relevant output in _quarto.yml and provide the site origin if document metadata uses relative image paths:

website:
  site-url: https://example.com
  open-graph: true
  twitter-card: true

Then add page-specific metadata to the Quarto document:

---
title: "A page title"
description: "A concise page summary"
image: "/images/page-preview.png"
---

Quarto derives title and description from page metadata by default. Its documented image options include a full URL, document-relative or project-relative path, an image marked .preview-image, and fallback discovery for included files named preview.png, feature.png, cover.png, or thumbnail.png. Relative paths and the .preview-image option require site-url. Optional image metadata includes image-width, image-height, and image-alt. See Quarto’s website tools documentation for the exact supported settings and precedence.

Hugo and Grafana Writers’ Toolkit

Grafana’s Writers’ Toolkit documents meta_image as its front matter field for social image metadata, and specifies a URL to an image hosted on the website:

---
title: "A page title"
meta_image: https://example.com/images/page-preview.png
---

This is Grafana’s documented convention, not a universal Hugo field. Check the theme or templates used by your Hugo site and confirm that they turn the field into head tags. See the Grafana Writers’ Toolkit metadata guidance.

Next.js

Next.js is not itself a generic Markdown front matter parser. Your content system must load and parse the Markdown, then pass its values to Next.js metadata or image-generation code. In the App Router, a Server Component page can export static metadata or implement generateMetadata for data-dependent values. For example, assuming the page has already loaded post from your content layer:

import type { Metadata } from 'next'

export async function generateMetadata(): Promise<Metadata> {
  const post = await loadPost()

  return {
    title: post.title,
    description: post.description,
    openGraph: {
      title: post.title,
      description: post.description,
      type: 'article',
      url: `https://example.com/articles/${post.slug}/`,
      images: [post.imageUrl],
    },
  }
}

loadPost is application-specific: implement it with your Markdown parser or content layer, and ensure post.imageUrl is the final public URL. Next.js metadata APIs are supported in Server Components. The framework also supports route image files such as opengraph-image and twitter-image; more specific route-level files take precedence over higher-level ones. For generated post artwork, a route-level opengraph-image.ts can return an ImageResponse based on page data. The Next.js example uses 1200 × 630 pixels and PNG, but that is an example rather than a universal platform requirement. Consult the metadata API and Open Graph image convention.

Jekyll

Jekyll parses YAML front matter at the start of a file between triple-dashed delimiters, and Liquid templates can read custom variables. You can store an image value there, but a project-specific field does not automatically become social metadata unless your theme or layout emits the tags:

---
title: "A page title"
description: "A concise page summary"
social_image: "/images/page-preview.png"
---

In the layout’s head template, map the variables to the required Open Graph properties and resolve the image to its deployed URL. The field name above is an example chosen for the project; confirm your theme’s conventions and generated output.

4. Choose static or generated images

A static image is straightforward when an editor creates a distinct asset for each post. A generated image is useful when cards follow a repeatable design and use page data such as the title. Next.js documents static route image files and dynamic image generation; Quarto documents page-selected images and fallback discovery.

  • Static asset: put the finished image in the site’s public assets, then reference its deployed URL or supported relative path in front matter.
  • Generated image: build an image during the site build or expose a framework-supported image route. Make sure the route can access the same page data as the metadata function.
  • Fallback image: configure a site-wide default for pages without their own image, where the framework supports defaults. Check that a page-specific value overrides it as intended.

Do not assume a single image size or format is accepted everywhere. The cited framework examples do not establish universal requirements across social services. Choose an appropriate landscape asset for your target platforms and verify the result with those platforms’ current preview tooling when available.

5. Validate the built page before publishing

  1. Build or render the site using its normal production configuration.
  2. Open the published page HTML or fetch it from the deployed site. Inspect the actual <head> for og:title, og:type, og:image, and og:url. Confirm the title, canonical page URL, and image belong to the same page.
  3. Copy the og:image value and request that URL directly. Confirm it resolves to an image response accessible without a login, private network, or browser-only session.
  4. If you emit Twitter Card metadata too, inspect that metadata separately. Do not assume Open Graph values are mirrored in every configuration.
  5. Repeat for a page with an explicit image, one relying on a default, and one with unusual characters or nested paths in its URL.

For a quick capture of the rendered public page while reviewing its appearance, ScreenshotNeo can capture a website as PNG, JPEG, WebP, or PDF. A screenshot helps inspect the rendered page; it does not replace checking the page source and image URL.

6. Troubleshoot common failures

Symptom Likely cause Fix
No preview image metadata appears The generator does not recognize the front matter key, or the page template does not emit social tags. Use the framework’s documented schema and enable/configure its metadata output. Inspect rendered HTML rather than the Markdown source.
The tag exists but points to the wrong location A relative path was resolved against a different base than expected, or the site origin is missing. Set the site origin where required, use the documented path convention, and inspect the final absolute og:image URL.
The image URL is correct locally but fails after deployment The asset was not copied to the published output, the URL uses a local filesystem path, or the production base path differs. Check the built asset tree and deployed URL. Reference a publicly served asset using the project’s deployment-aware path rules.
The preview shows an old image The rendered metadata or asset may be cached by the consuming platform; cache refresh behavior varies and is not guaranteed by the framework. First verify the live HTML and asset. Then use the target platform’s current preview/debug workflow if it offers one; allow for platform-side caching.
Image works in a browser but not for a crawler The URL may require authentication, depend on a session, or be unavailable to external requests. Use a public asset URL and test it without logged-in browser state. Avoid private or temporary URLs.
Title or description differs across pages Global defaults, page overrides, route precedence, or the content loader may be supplying different values. Trace the final values from parsed front matter through the framework metadata API, then inspect the rendered tags for each route.
Next.js does not find the Markdown fields Next.js metadata does not parse Markdown front matter by itself. Load the content through the project’s parser/content layer and explicitly map the parsed values into metadata or the image route.

7. Performance, reliability, and cost considerations

Static image files add a straightforward asset lookup to page delivery. Dynamic image generation can centralize a card design, but it adds a route or build step whose access to page data and output must be kept reliable. Whichever approach you choose, make the image a stable public asset and preserve the same page-to-image mapping across rebuilds.

For reliability, keep a usable default where supported, validate metadata as part of publishing, and verify both the HTML and asset after deployment. A successful local build does not establish that the deployed image URL is public or correct. Avoid assuming a crawler cache will refresh immediately; the reviewed framework and protocol documentation does not define a universal refresh schedule.

There is no named cost or performance benchmark in the cited documentation for this workflow. Your practical costs depend on how your site builds or generates assets and serves them. A static file avoids per-request image generation in your application; a dynamic endpoint may need compute and caching according to your hosting setup.

Or skip the browser setup

If you need a screenshot of the rendered page while checking a social preview, ScreenshotNeo takes one GET request. Its API documentation covers the capture options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/articles/example/ -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com/articles/example/"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com/articles/example/',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res); // For Bun; in Node.js, write the response bytes with node:fs/promises.

ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. Sign up free for ScreenshotNeo.

FAQ

Does Markdown front matter automatically create a social preview?

No. It supplies data to a generator or rendering process. The framework or your templates must emit the corresponding metadata in the rendered document head.

Can I use the same front matter field in every framework?

No. Quarto documents image, Grafana documents meta_image, and Next.js uses metadata APIs or route image conventions. Check the project’s actual content and theme setup.

Should I store a full URL or a relative image path?

Follow the framework’s documented path rules. A full public URL is explicit; relative paths can work when the site origin and path resolution are configured correctly.

Is 1200 × 630 the required size?

The Next.js documentation uses those dimensions in an example. The reviewed sources do not establish that size as a universal requirement for every platform.

How can I tell whether the setup is correct?

Inspect the deployed page’s generated head tags and request the final og:image URL directly. Verify the intended page and image values, not only the source front matter.

Sources