ScreenshotNeo

BlogHow-to

How to Create Video Thumbnail Previews in Gatsby

Show a still poster or an interactive video preview in Gatsby. Compare local, hosted, and build-time workflows, with accessible React examples and fixes.

By the ScreenshotNeo team4 October 20268 min read

A Gatsby video thumbnail preview can mean either a still image displayed before playback or a short video that plays while someone browses. For a still image, set the native HTML <video poster="..."> attribute. For a moving preview, keep that poster as the initial state, then use React pointer events to play a muted video and pause it when the pointer leaves. In both cases, provide a keyboard-operable way to play and pause; hover alone is not enough.

Gatsby does not automatically create a polished poster image for every video. You can prepare the image yourself, generate frames in a media-processing build step, or use a hosted media workflow. Native HTML is the simplest starting point; plugins add build dependencies and need compatibility checks.

1. Choose a preview workflow

Approach Use it when Tradeoffs
Static poster You need a deliberate still image before playback. Simple native HTML; you provide the poster asset.
Pointer or hover preview Motion helps people scan a gallery. Requires handling touch, keyboard, reduced motion, and video loading.
Build-time frame extraction You have many local videos and want generated poster files. Adds FFmpeg, build time, cache, and plugin compatibility concerns.
Hosted media You already upload and deliver video through a media service. Depends on the vendor’s delivery, API, plan, and current terms.

Gatsby documents both local HTML5 video and third-party hosted embeds. A local video can expose several formats with multiple <source> elements. Choose based on where assets live, who generates the poster, what browser formats you need, and whether preview motion is useful. Gatsby’s video guide is the baseline for its media options.

2. Add a static poster to a Gatsby video

For many pages, the native poster attribute is all you need. Import local assets according to your project’s Gatsby and bundler configuration, or use hosted URLs.

import videoFile from "../assets/demo.mp4"
import posterImage from "../assets/demo-poster.jpg"

export default function DemoVideo() {
  return (
    <video controls poster={posterImage} preload="metadata" width="640">
      <source src={videoFile} type="video/mp4" />
      Your browser does not support embedded video.
    </video>
  )
}

The poster is the preview image; it is not a frame extracted automatically by Gatsby. Prepare it as an image asset or create it through a media workflow. With hosted media, the same markup can use a URL for the src and poster values. If you include multiple formats, each source’s MIME type must match the actual file.

preload="metadata" asks the browser for metadata rather than signaling that the whole file should be downloaded up front. Other values are none and auto. These are hints, and browsers may not follow them exactly; auto permits downloading the whole file. See the MDN video element reference.

3. Add a moving preview on pointer entry

A moving preview is playback triggered by interaction, not automatic thumbnail generation. This React component keeps the poster visible as the initial state, starts muted playback on pointer entry, and pauses and rewinds on exit. It also leaves native controls available for keyboard and touch users.

import { useRef } from "react"

export function VideoPreview({ src, poster, title }) {
  const videoRef = useRef(null)

  async function startPreview() {
    const video = videoRef.current
    if (!video || window.matchMedia("(prefers-reduced-motion: reduce)").matches) return
    video.muted = true
    try {
      await video.play()
    } catch {
      // Playback can be blocked; native controls remain available.
    }
  }

  function stopPreview() {
    const video = videoRef.current
    if (!video) return
    video.pause()
    video.currentTime = 0
  }

  return (
    <figure onPointerEnter={startPreview} onPointerLeave={stopPreview}>
      <video
        ref={videoRef}
        controls
        muted
        playsInline
        preload="none"
        poster={poster}
        aria-label={title}
        onBlur={stopPreview}
      >
        <source src={src} type="video/mp4" />
        Your browser does not support embedded video.
      </video>
      <figcaption>{title}</figcaption>
    </figure>
  )
}

This uses pointer events so it is not limited to a mouse. On touch screens, pointer entry may not represent a useful preview interaction, so test on target devices and let users activate the visible controls. A more controlled design can use an explicit Preview button and start playback only on activation. The example stops on blur to avoid leaving motion running after keyboard focus moves away.

Cloudinary’s Gatsby tutorial demonstrates the basic mouse-enter/mouse-leave pattern with muted playback. Treat it as an interaction example rather than a complete accessible player: Cloudinary’s Gatsby video preview tutorial.

4. Generate poster frames at build time

If you have many local videos, a build pipeline can extract frames with FFmpeg. Gatsby community plugin listings describe screenshot extraction and video transforms, but their stated compatibility is narrow and may not match a current project. The gatsby-transformer-video listing describes a beta plugin supporting Gatsby v4.1.4 and warns that conversion can take a long time; it recommends caching generated output. gatsby-plugin-ffmpeg describes a low-level helper for Gatsby v4.

  1. Check the plugin’s supported Gatsby and Node versions against your project.
  2. Confirm that FFmpeg and ffprobe can be installed or run in your build environment.
  3. Read the plugin’s current configuration and identify where generated files and caches live.
  4. Run a production build in the same kind of environment used for deployment.
  5. Inspect output posters and add accessible fallback text and video controls.

The gatsby-video listing also illustrates a component workflow using transformed local variants and a poster. A plugin example does not establish support for every Gatsby release, so verify current maintenance and compatibility before adopting one.

5. Accessibility, formats, and loading behavior

  • Keep a non-hover route. Provide visible controls or a keyboard-operable play action. Do not make hover the only way to play or understand the video.
  • Respect reduced motion. Avoid automatically animating previews when the user requests reduced motion; the example checks the system preference.
  • Use captions and descriptions. Gatsby’s video guidance recommends accessible controls, captions, transcripts, and audio descriptions where appropriate.
  • Choose preload deliberately. Use none to defer loading, metadata when duration or dimensions are useful, or auto only when early download is acceptable. The browser treats this as a hint.
  • Supply valid formats. Add source elements only for formats you actually provide, with matching types, and test the browsers and devices you support.
  • Do not confuse poster delivery with video delivery. A poster can appear before playback, but loading or previewing the video still has network and decoding costs.

For custom players, Gatsby’s video and accessibility guidance provides a useful checklist.

6. Troubleshoot common problems

Symptom Likely cause Fix
Poster is missing Incorrect asset path, unsupported URL, or image request failure. Check the rendered URL in the browser, confirm the image is deployed, and verify the asset import method for your Gatsby setup.
Video shows a blank box No poster is supplied yet, video metadata has not loaded, or the media request failed. Set a poster explicitly, check the network request and server response, and provide fallback content.
Hover preview does not play Browser playback policy, rejected play promise, source error, or unsupported format. Keep playback muted, handle the play promise rejection, verify the media request and codec, and retain native controls.
Preview continues after leaving The leave handler is not attached to the same region or state is not reset. Pause on pointer leave and on focus loss; reset currentTime if each preview should restart.
Touch users cannot find playback The design depends on hover. Keep controls visible or add a keyboard and touch-operable Preview button.
Build fails in CI Plugin version mismatch, missing FFmpeg binary, unavailable download, or incompatible Node/Gatsby version. Check the plugin’s supported versions and CI binary access; test the production build in the deployment environment or use a prepared poster.
Builds become slow Video conversion and frame extraction repeat for unchanged assets. Use the plugin’s documented cache, avoid unnecessary transforms, and generate only the frames you need.
Large network usage Preload settings or multiple simultaneous previews cause video data to load early. Prefer preload="none" or metadata, start one preview at a time, and measure requests on representative pages.

7. Performance, reliability, and cost

A static poster is usually the simplest way to give each card a predictable initial image; it does not remove the cost of serving the poster asset. Moving previews can trigger video requests, decoding, and concurrent work, so limit how many play at once and choose preload behavior intentionally. There are no head-to-head performance or cost benchmarks in the cited sources, so measure your own page with realistic media and network conditions.

Build-time extraction shifts work to the build and can make builds longer; caching is important when the same source files are processed repeatedly. Hosted media can simplify asset delivery, but it makes your page depend on that vendor’s service and current plan, limits, and terms. No particular provider pricing or savings claim is established here.

8. Or skip the browser setup

If the task is to capture a webpage as an image or PDF for a Gatsby workflow, ScreenshotNeo is a website screenshot API and MCP server. It is separate from extracting a frame from a video file: use the video workflows above for video posters. One GET request captures a webpage; see the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
  • Cookie and consent banners are accepted like a visitor, then more than 60 known consent platforms, newsletter popups, and chat widgets are removed before capture; each step can be turned off.
  • Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. Response headers report the page verdict and billing status.
  • An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
  • 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000. Every feature is on every plan.

Sign up for 1,000 free screenshots a month, with no card required.

9. Frequently asked questions

Does Gatsby create a video thumbnail automatically?

No. Supply a poster image or configure a media-processing workflow to generate one.

Is a poster the same as a hover preview?

No. A poster is a still image shown before playback. A hover preview plays the video in response to pointer interaction.

Can I use a hosted video URL?

Yes. Gatsby documents hosted video alongside local HTML5 video. Confirm the host serves a browser-supported format and that the poster URL is reachable.

Should I use an FFmpeg Gatsby plugin?

Only after checking its current Gatsby and Node compatibility, maintenance, binary requirements, and caching behavior against your build environment.