How to Add Open Graph Tags to Next.js, React, and WordPress
You've created a great OG image. Now you need to actually add the Open Graph meta tags so platforms know about it. The implementation varies significantly between Next.js (App Router), React (SPAs with react-helmet), and WordPress — and getting it wrong means your images won't show up when shared.
This tutorial covers the exact code for each framework with copy-paste examples, common pitfalls, and how to use BrandSnap to generate the images themselves. Let's get your links looking professional on every platform.
Table of Contents
- Quick Reference: Essential OG Tags
- Next.js App Router (Metadata API)
- Next.js: Dynamic OG Images with generateMetadata
- React SPA with react-helmet-async
- React SSR Considerations
- WordPress: Plugin Approach (Yoast / Rank Math)
- WordPress: Manual Implementation
- Generating the OG Images with BrandSnap
- Testing Your Implementation
- Common Mistakes & Troubleshooting
Quick Reference: Essential OG Tags
Before diving into framework-specific implementations, here are the meta tags you need on every page:
<!-- Required Open Graph Tags --> <meta property="og:title" content="Page Title" /> <meta property="og:description" content="Page description (150-160 chars)" /> <meta property="og:image" content="https://yoursite.com/og-image.png" /> <meta property="og:image:width" content="1200" /> <meta property="og:image:height" content="630" /> <meta property="og:image:alt" content="Description of the image" /> <meta property="og:url" content="https://yoursite.com/page" /> <meta property="og:type" content="website" /> <meta property="og:site_name" content="Your Site Name" /> <!-- Twitter Card Tags --> <meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:title" content="Page Title" /> <meta name="twitter:description" content="Page description" /> <meta name="twitter:image" content="https://yoursite.com/og-image.png" /> <meta name="twitter:image:alt" content="Description of the image" />
⚠️ Critical rule: The og:image URL must be absolute (starting with https://) and served over HTTPS. Relative paths like /images/og.png will not work on any platform.
Next.js App Router (Metadata API)
Next.js 13+ App Router has a built-in Metadata API that's the cleanest way to add OG tags. Export a metadata object from any page.tsx or layout.tsx:
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'My Blog Post Title',
description: 'A compelling description of your blog post.',
openGraph: {
title: 'My Blog Post Title',
description: 'A compelling description of your blog post.',
url: 'https://yoursite.com/blog/my-post',
type: 'article',
publishedTime: '2026-06-15T00:00:00Z',
authors: ['Your Name'],
images: [
{
url: 'https://yoursite.com/og/my-post.png',
width: 1200,
height: 630,
alt: 'My Blog Post Title',
},
],
},
twitter: {
card: 'summary_large_image',
title: 'My Blog Post Title',
description: 'A compelling description of your blog post.',
images: ['https://yoursite.com/og/my-post.png'],
},
}
export default function MyPost() {
return (
<article>
<h1>My Blog Post Title</h1>
{/* ... */}
</article>
)
}Next.js automatically renders these as the correct <meta> tags in the page's <head>. No need to manually write HTML meta tags.
Setting Global Defaults in layout.tsx
Set site-wide defaults in your root layout. Page-level metadata will override these:
import type { Metadata } from 'next'
export const metadata: Metadata = {
metadataBase: new URL('https://yoursite.com'),
title: {
default: 'Your Site Name',
template: '%s | Your Site Name',
},
description: 'Your site description.',
openGraph: {
type: 'website',
locale: 'en_US',
url: 'https://yoursite.com',
siteName: 'Your Site Name',
images: [
{
url: '/og-default.png', // metadataBase makes this absolute
width: 1200,
height: 630,
alt: 'Your Site Name',
},
],
},
twitter: {
card: 'summary_large_image',
site: '@youraccount',
},
}💡 Pro tip: Setting metadataBase in your root layout lets you use relative paths for images in child pages. Next.js will automatically resolve them to absolute URLs.
Next.js: Dynamic OG Images with generateMetadata
For dynamic pages (blog posts from a CMS, product pages, etc.), use the generateMetadata function to fetch data and generate OG tags dynamically:
import type { Metadata } from 'next'
type Props = {
params: Promise<{ slug: string }>
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params
const post = await getPostBySlug(slug) // Your data fetching
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
url: `https://yoursite.com/blog/${slug}`,
type: 'article',
publishedTime: post.publishedAt,
images: [
{
url: post.ogImage, // From your CMS or BrandSnap
width: 1200,
height: 630,
alt: post.title,
},
],
},
twitter: {
card: 'summary_large_image',
title: post.title,
description: post.excerpt,
images: [post.ogImage],
},
}
}
export default async function BlogPost({ params }: Props) {
const { slug } = await params
const post = await getPostBySlug(slug)
return (
<article>
<h1>{post.title}</h1>
{/* ... */}
</article>
)
}Adding JSON-LD Structured Data
For maximum SEO benefit, pair OG tags with JSON-LD structured data. Add it as a script tag in your component:
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: post.title,
description: post.excerpt,
image: post.ogImage,
datePublished: post.publishedAt,
author: {
'@type': 'Person',
name: post.author,
},
}
// In your component's JSX:
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>React SPA with react-helmet-async
For React single-page applications (Create React App, Vite, etc.), use react-helmet-async to manage meta tags in the document head:
npm install react-helmet-async
Step 1: Wrap Your App with HelmetProvider
import { HelmetProvider } from 'react-helmet-async'
ReactDOM.createRoot(document.getElementById('root')!).render(
<HelmetProvider>
<App />
</HelmetProvider>
)Step 2: Create a Reusable SEO Component
import { Helmet } from 'react-helmet-async'
interface SEOProps {
title: string
description: string
image: string
url: string
type?: string
publishedTime?: string
author?: string
}
export function SEO({
title,
description,
image,
url,
type = 'website',
publishedTime,
author,
}: SEOProps) {
return (
<Helmet>
{/* Primary Meta Tags */}
<title>{title}</title>
<meta name="description" content={description} />
{/* Open Graph */}
<meta property="og:type" content={type} />
<meta property="og:url" content={url} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={image} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content={title} />
<meta property="og:site_name" content="Your Site Name" />
{publishedTime && (
<meta property="article:published_time" content={publishedTime} />
)}
{author && (
<meta property="article:author" content={author} />
)}
{/* Twitter Card */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:url" content={url} />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={image} />
<meta name="twitter:image:alt" content={title} />
</Helmet>
)
}Step 3: Use It in Your Pages
import { SEO } from '../components/SEO'
export function BlogPost({ post }) {
return (
<>
<SEO
title={post.title}
description={post.excerpt}
image={`https://yoursite.com/og/${post.slug}.png`}
url={`https://yoursite.com/blog/${post.slug}`}
type="article"
publishedTime={post.publishedAt}
author={post.author}
/>
<article>
<h1>{post.title}</h1>
{/* ... */}
</article>
</>
)
}React SSR Considerations
⚠️ Important: SPAs and Social Crawlers
Here's the catch with React SPAs: most social media crawlers (Facebook, Twitter, LinkedIn) do not execute JavaScript. They fetch the raw HTML and read the meta tags directly. If your OG tags are only rendered client-side via react-helmet, crawlers won't see them.
Solutions:
- 1.Server-Side Rendering (SSR): Use Next.js, Remix, or a custom SSR setup so meta tags are in the initial HTML response.
- 2.Pre-rendering: Use tools like
react-snaporprerender.ioto generate static HTML snapshots that crawlers can read. - 3.Static Site Generation (SSG): If your content is static, use a framework with SSG support (Next.js, Gatsby, Astro) to pre-render pages with OG tags baked in.
Our recommendation: If you're starting a new React project and care about social sharing, use Next.js with the App Router. The metadata API handles SSR automatically, and you never have to worry about crawlers missing your OG tags.
WordPress: Plugin Approach (Yoast / Rank Math)
The easiest way to add OG tags to WordPress is with an SEO plugin. Here's how to set it up with the two most popular options:
Yoast SEO
Install and activate Yoast SEO from Plugins → Add New.
Go to Yoast SEO → Social in the admin sidebar. Enable the Open Graph meta data checkbox under the Facebook tab.
Edit any post or page. Scroll to the Yoast SEO meta box, click the "Social" tab, and upload your OG image for that specific page.
Set a default OG image under Yoast → Social → Facebook tab for pages that don't have a specific image.
Rank Math
Install and activate Rank Math. Run the setup wizard.
Go to Rank Math → General Settings → Social. Configure default OG image and other social settings.
On any post/page editor, click the Rank Math icon in the sidebar, go to "Social" tab, and set the OG image for that specific page.
💡 Tip: Both Yoast and Rank Math let you set different images for Facebook/OG and Twitter. Use this if you want to optimize image cropping for each platform. Generate both versions with BrandSnap's OG generator.
WordPress: Manual Implementation
If you prefer not to use a plugin (for performance or simplicity), add OG tags directly in your theme's functions.php:
function add_og_meta_tags() {
if (is_single() || is_page()) {
global $post;
$title = get_the_title();
$description = has_excerpt()
? get_the_excerpt()
: wp_trim_words(get_the_content(), 30);
$url = get_permalink();
$site_name = get_bloginfo('name');
// Get OG image: featured image or fallback
if (has_post_thumbnail()) {
$image_id = get_post_thumbnail_id();
$image = wp_get_attachment_image_src($image_id, 'full');
$image_url = $image[0];
$image_width = $image[1];
$image_height = $image[2];
} else {
// Fallback to a default OG image
$image_url = get_template_directory_uri() . '/og-default.png';
$image_width = 1200;
$image_height = 630;
}
echo '<meta property="og:title" content="' . esc_attr($title) . '" />';
echo '<meta property="og:description" content="' . esc_attr($description) . '" />';
echo '<meta property="og:image" content="' . esc_url($image_url) . '" />';
echo '<meta property="og:image:width" content="' . esc_attr($image_width) . '" />';
echo '<meta property="og:image:height" content="' . esc_attr($image_height) . '" />';
echo '<meta property="og:url" content="' . esc_url($url) . '" />';
echo '<meta property="og:type" content="article" />';
echo '<meta property="og:site_name" content="' . esc_attr($site_name) . '" />';
// Twitter Card
echo '<meta name="twitter:card" content="summary_large_image" />';
echo '<meta name="twitter:title" content="' . esc_attr($title) . '" />';
echo '<meta name="twitter:description" content="' . esc_attr($description) . '" />';
echo '<meta name="twitter:image" content="' . esc_url($image_url) . '" />';
}
}
add_action('wp_head', 'add_og_meta_tags');This approach uses the post's featured image as the OG image. Upload your BrandSnap-generated images as featured images, and they'll automatically be used as OG images.
Using Custom Fields for More Control
For a separate OG image field (distinct from your featured image), add a custom meta box:
// In the og_meta_tags function, replace the image logic:
$custom_og = get_post_meta($post->ID, 'og_image_url', true);
if ($custom_og) {
$image_url = $custom_og;
$image_width = 1200;
$image_height = 630;
} elseif (has_post_thumbnail()) {
// ... featured image fallback
}Generating the OG Images with BrandSnap
Now that you know how to implement the tags, you need the actual images. BrandSnap generates professional OG images in seconds — here's how it fits into each workflow:
For Next.js Projects
- 1.Generate your OG image at www.brandsnap.io/tools/og-image-generator
- 2.Download the PNG and place it in your
public/og/directory - 3.Reference it in your metadata:
images: [{url: '/og/my-post.png'}] - 4.
metadataBaseautomatically makes it an absolute URL
For React SPAs
- 1.Generate images with BrandSnap for each key page
- 2.Host them on your CDN or in your
public/folder - 3.Pass the full URL to your SEO component
- 4.Remember: use SSR or pre-rendering so crawlers can see the tags
For WordPress
- 1.Generate images with BrandSnap for each post/page
- 2.Upload as the featured image (or to the Yoast/Rank Math OG image field)
- 3.Your SEO plugin or custom code handles the rest
For Teams with BrandSnap API (Agency Plan)
Automate the entire process: generate OG images as part of your build or CMS workflow.
// In your build script or CMS webhook:
async function generateOGImage(pageUrl: string, slug: string) {
const res = await fetch('https://api.brandsnap.io/v1/generate', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BRANDSNAP_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: pageUrl,
type: 'og-image',
style: 'neo-brutal',
}),
})
const { imageUrl } = await res.json()
// Download and save to your public directory
const image = await fetch(imageUrl)
const buffer = await image.arrayBuffer()
await fs.writeFile(`public/og/${slug}.png`, Buffer.from(buffer))
}Testing Your Implementation
After adding OG tags, always validate before going live. Here's the testing checklist:
1. View Page Source
Right-click your deployed page → View Page Source. Search for og:image. The URL should be absolute, start with https://, and the image should load when you paste it in a browser.
2. Use Platform Validators
Test with the Facebook Debugger, Twitter Card Validator, and LinkedIn Post Inspector.
3. Check the Image Loads
Copy the og:image URL and open it directly in your browser. It should load quickly (under 3 seconds) and display the correct image. Check that the server returns the right Content-Type header (image/png or image/jpeg).
4. Test on Actual Platforms
Send your URL to yourself in Slack, Discord, or Twitter DMs. The preview should show your custom image, title, and description. If it shows a cached old version, use the platform's debugger to force a refresh.
Common Mistakes & Troubleshooting
Image not showing on social media
Cause: Relative URL, HTTP instead of HTTPS, or slow server response. Fix: Use absolute HTTPS URLs. Ensure image loads in <5 seconds. Clear platform cache with their debugger tool.
Old image showing after update
Cause: Platform caching. Social platforms cache OG images aggressively. Fix: Use the Facebook Debugger / LinkedIn Post Inspector to force re-scrape. Alternatively, change the image URL (add a query param like ?v=2).
Image looks cropped or wrong aspect ratio
Cause: Image isn't 1200×630 or critical content is near edges. Fix: Use exactly 1200×630 pixels. Keep important content in the center 80%. Include og:image:width and og:image:height tags.
React SPA: tags not visible to crawlers
Cause: Client-side-only rendering. Crawlers don't execute JavaScript. Fix: Use Next.js (SSR), pre-render with react-snap, or a pre-rendering service like Prerender.io.
WordPress: conflicting OG tags
Cause: Multiple plugins or themes outputting OG tags. Fix: Check View Source for duplicate og:image tags. Disable OG output in your theme if using Yoast/Rank Math, or vice versa.
Need the OG Images? Generate Them in Seconds
You now know how to implement the tags. Let BrandSnap handle the images — professional, brand-matched, and perfectly sized for every platform.
Related Articles
How to Create OG Images for Your Website (2026 Guide) →
Complete guide covering OG image specs, tools, and implementation strategies.
Social Media Preview Images: The Complete 2026 Guide →
Exact image sizes, formats, and best practices for every social platform.
BrandSnap vs Canva for OG Images: Which Is Better for Developers? →
Honest comparison for the developer and SaaS use case.