CLI tool
nuxt-modules/og-image avatar
nuxt-modules/og-image

nuxt-og-image: Open Graph images from Vue templates in a Nuxt module

Generate OG Images with Vue templates in Nuxt.

551 stars51 forksTypeScriptNOASSERTION

At a glance

What is it?
nuxt-og-image is a Nuxt module that renders an og:image for each route either from a Vue component or from a screenshot of the page itself, using Satori, Takumi or a headless browser. It is the workhorse behind the Nuxt SEO ecosystem and it ships a DevTools playground for iterating on the design.
Who is it for?
Adopt nuxt-og-image if you run a Nuxt site whose social previews currently come from a static image, since per-route templates in Vue and a screenshot mode cover most preview needs without a separate rendering service. Do not adopt it if you need images on a framework other than Nuxt, or if you have no Node runtime available at build or request time, because the module is a Nuxt module and its rendering paths are server side.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Two ways to produce a preview image, and the choice between them

The README describes the module in one line: it lets you generate OG Images using either Vue components or screenshots of your pages. That fork in the road is the single most important decision in the project, and it runs through every other feature.

The component route gives you a Vue file as the template, which is then rendered to an image. The documentation names two renderers for it, Satori and Takumi, and both work with Tailwind or UnoCSS using your own theme, plus Google fonts and six emoji families. The advantage is control: you have a component tree, so conditional layout, a loop over tags, and props pulled from your route are all ordinary Vue.

The screenshot route is the one described as the lazy option, and the README's phrasing is that you can generate screenshots for every page, with the ability to hide elements and wait for animations. A screenshot is attractive because it is automatically consistent with the live page, and for a documentation site or a blog where the article layout already looks good, there is little reason to design a second visual language.

The third path, prerendering using the browser, is offered as supporting painless, complex templates. That is a separate capability from the screenshot feature: it uses a real browser engine to produce the image, which is what makes heavy CSS or web fonts work at the cost of a heavier runtime.

The docs point newcomers at the Mastering Open Graph Tags guide for the background on why the tag matters, and the stated claim is that OG Images are known to improve click-through rates on social media.

Installing the module in a Nuxt project

The installation is the standard Nuxt module command, and the README describes it as installing the nuxt-og-image dependency to your project.

bash
npx nuxt module add og-image

That command is the whole documented install step. There is no separate CLI to configure, no key to obtain and no account to create, which is a notable contrast with the managed image services that charge per render.

The package is published as nuxt-og-image and the package.json declares it as an ES module with a Nuxt module build. The engines field is strict, requiring Node ^22.22.3 || ^24.15.0 || >=26.0.0, so a project still on Node 20 will not pass the engine check, and that is worth catching before you start rather than during a build.

The package also exposes a bin entry, nuxt-og-image pointing at ./bin/cli.mjs, which is what the module add command invokes. Two export paths are published, the module itself at ./dist/module.mjs and a separate ./content entry at ./dist/content.mjs, the latter being the integration point for projects that render images from Nuxt Content documents.

Full configuration lives in the documentation at nuxtseo.com, and the README also points out that a skill for the module is published on skilld.dev for people driving the setup with an AI agent.

The DevTools playground and how iteration actually works

The feature list says you can design and test your og:image in the Nuxt DevTools OG Image Playground with full HMR. This is the part of the project that decides whether you will keep using it after the first week.

Social preview images are unusual in that the feedback loop is normally brutal. You write a template, deploy, post a link in a chat, wait for the crawler to refetch, and hope the platform does not cache the old image. A DevTools panel with hot reload removes every one of those steps except the last. You edit the Vue component and see the rendered image immediately in the browser, which is the difference between a five-minute change and a one-hour change.

The repository layout supports that claim. There is a devtools/ directory, and the build script for it is a separate step, build:client, which copies the devtools directory into dist/devtools after the module build. That is a small design detail with a practical consequence: the playground ships as part of the package, so a consumer gets it from the same install rather than assembling it from separate pieces.

There is also a playground/ directory in the tree, which is the module's own development environment, and the dev script runs nuxt dev playground, so the maintainer develops the module and its templates in a real Nuxt app rather than in a synthetic harness.

There is a snapshots script, node --import jiti/register scripts/generate-template-snapshots.ts, which generates reference images for the templates so a change to the rendering pipeline shows up as a diff rather than as a silent shift in output.

Running on the edge, and what that costs

The features list states that the module works on the edge, naming Vercel Edge, Netlify Edge and Cloudflare Workers. For a preview image generator that is the whole point, because the image has to be produced somewhere and the somewhere closest to the request is the one that serves it fast.

Edge support is also where the design trade-offs show up. The repository contains a bench/ directory and a script named bench:edge-startup that builds a fixture project with nuxt build bench/fixtures/edge-startup and then runs the resulting edge function through node bench/edge-startup.mjs. A dedicated startup benchmark for the edge case tells you the author treats cold start as the binding constraint for this module, which is the right constraint to treat it as, since a preview image that takes two seconds to appear after a deploy is a preview image that gets shared late.

The contrast between renderers matters here. Satori converts a virtual DOM tree into an SVG and then rasterises it, which is fast and self-contained but constrained by what it can lay out. A browser-based renderer can reproduce anything a browser can draw, and costs a full browser engine to do it. The module supports both paths, and the practical consequence is that the screenshot and browser-prerender routes need a runtime with a browser available, while the Satori and Takumi routes do not.

That distinction decides whether the module can live in your deployment at all. A serverless function with a 128 MB memory limit cannot host a headless browser, so the screenshot features are only usable where you have a persistent Node server or a platform that provides one.

The examples, and the four situations they cover

The README links four runnable demos, and the choice of four tells you what the module is actually used for.

There is a basic-satori example and a basic-takumi example, which are the same minimal custom template rendered through the two different engines, so you can see what each one gives you on the same design. There is a Nuxt Content example, which matches the ./content export path in package.json and covers the case where the image data comes from a Markdown frontmatter field rather than from route metadata. And there is an i18n example, which covers the case of per-locale images.

The i18n case is the one that surprises people who build social previews casually. A site with several locales usually needs a different image per language, which means the route key has to include the locale and the renderer has to receive the right locale data, and a template that hard-codes a single language produces the wrong image for every locale except one. Seeing an example for it is a sign the author has hit that problem.

All four demos run as StackBlitz projects pointing at the same repository tree, so they are the fastest way to see what a configuration looks like end to end before you copy it into your own project. The examples/ directory in the repository mirrors these, and the docs/ directory holds the written documentation that the README links to the website for.

For teams already using Nuxt Content, the ./content export is the detail to note, since it means the module can read image properties from your content files without you writing a separate route rule for each document.

Licensing, versioning and the maintenance picture

There is an inconsistency in the licence information that a careful adopter should note. The repository metadata classifies the licence as Other, a custom licence GitHub cannot classify, while the README states that the project is licensed under the MIT license and the package.json declares the licence field as MIT, pointing at LICENSE.md. The package manifest and the README agree with each other, so the MIT claim is the one to rely on, but the file itself is worth reading directly before you build a commercial dependency on it, since the metadata classification suggests the file did not parse as a standard licence identifier.

The release cadence is fast and recent. The recent releases are v7.0.7 on 2026-10-14, v7.0.6 on 2026-10-13 and v7.0.5 on 2026-10-13, three releases inside a single day, and the last push was on 2026-10-20. This is an actively developed module, and the consequence for you is that a major version boundary exists to absorb breaking changes rather than shipping them silently, and the module.json based Nuxt module packaging means Nuxt will warn you when a required version range is not satisfied.

The version in package.json is 7.0.7, matching the latest tag, and the project uses pnpm as its package manager with [email protected] declared, along with a pnpm-workspace.yaml and a pnpm-lock.yaml. The workspace layout, with separate directories for src, devtools, docs, examples, playground, bench, skills and test, means the published package is a small subset of the repository: the files array publishes only bin, dist, skills and types/virtual.d.ts, so the docs and examples do not ship to your node_modules.

Where it stops being the right choice

There are three limits, and the first is the framework coupling.

The first is that this is a Nuxt module, not a library. It hooks into the Nuxt module system, into the DevTools panel and into the Nitro server, and there is no documented path to use it from a plain Vue app, a React app or a static site generator. If you are not on Nuxt, the Satori and Takumi renderers themselves are usable directly and this module is not the route to them.

The second is that generating an image is a rendering job, and rendering jobs have failure modes that image services hide. A template that references a font, an external image or a data source can fail at request time rather than at build time, and a failure means no og:image on that page, which social platforms treat as no preview. The screenshot and browser routes add a second class of risk, since they depend on the page rendering successfully in a headless browser, including any client-side data fetch that page makes. There is a bench directory measuring startup for exactly this class of concern, but a benchmark is not a guarantee that every route in your app renders.

The third is the cost of a per-route image on a large site. One image per article is one render per article, and if you use the browser route, that is one browser invocation per article. On a site with thousands of pages that is a real infrastructure bill or a real build time, and the sensible pattern is prerendering at build time rather than rendering on request, which is a choice you have to make explicitly rather than a default you inherit.

The honest alternative is a hosted image service that renders your template on their infrastructure and gives you a URL, which removes the runtime and the rendering risk from your stack at the price of a per-render fee, a third-party dependency and a template that has to be written for their API rather than for Vue. A second alternative, for the simplest case, is a static image per page, which costs nothing to serve and never fails at request time but also never varies with the content of the page. nuxt-og-image earns its place in the middle, where the image is per-route and derived from real data, and it earns it precisely because the template is a Vue component you already know how to write.

Editorial conclusion

Adopt nuxt-og-image if you run a Nuxt site whose social previews currently come from a static image, since per-route templates in Vue and a screenshot mode cover most preview needs without a separate rendering service. Do not adopt it if you need images on a framework other than Nuxt, or if you have no Node runtime available at build or request time, because the module is a Nuxt module and its rendering paths are server side. Before you commit, render one page in the DevTools playground and check the resulting image against the social platforms you actually post to, and note the last push was on 2026-10-20 with release v7.0.7 on 2026-10-14.

Frequently asked questions

What is nuxt-og-image and what does it do?

It is a Nuxt module that generates Open Graph images using either Vue components or screenshots of your pages, rendering templates with Satori or Takumi, with Tailwind or UnoCSS, Google fonts and six emoji families, or prerendering through the browser. It also provides a DevTools OG Image Playground with full HMR for designing and testing the image.

How do I install nuxt-og-image?

Run npx nuxt module add og-image in your project, which installs the nuxt-og-image dependency. The package requires Node ^22.22.3 || ^24.15.0 || >=26.0.0 according to its engines field, and the full configuration is documented at nuxtseo.com/og-image.

Does nuxt-og-image work on the edge?

Yes, the features list states that it works on the edge, naming Vercel Edge, Netlify Edge and Cloudflare Workers. The repository includes an edge startup benchmark script and a bench/fixtures/edge-startup fixture, which indicates that cold start is treated as the main constraint for this module.

What is the difference between the Satori and Takumi rendering options?

Both are template renderers for Vue components, and both work with Tailwind or UnoCSS using your theme, Google fonts and emoji families. The repository ships parallel minimal examples for each, basic-satori and basic-takumi, so you can compare their output on the same design before choosing.

Can nuxt-og-image generate a different image per page or locale?

Yes. Per-route images are the point of the module, and the README links four demos covering a custom template with Satori, one with Takumi, Nuxt Content driven images and an i18n example for per-locale images. The package also publishes a separate ./content export for projects that read image properties from Nuxt Content documents.

Official sources

  1. Issues
  2. nuxt-modules/og-image on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/nuxt-modules-og-image.svg)](https://hysenlabs.com/projects/nuxt-modules-og-image)

Was this useful?