When you share a URL on LinkedIn, Twitter, or Slack, what determines the title, description, and image that appear in the preview card? That’s OpenGraph — a protocol created by Facebook in 2010 that has since become the standard for social metadata across the web.
Without it, platforms guess. With it, you control exactly what people see when they share your work.
What is OpenGraph?
OpenGraph is a set of <meta> tags placed in the <head> of your HTML that define how a page should appear when shared. The core properties:
<meta property="og:title" content="Your page title" />
<meta property="og:description" content="A short description" />
<meta property="og:image" content="https://yoursite.com/preview.png" />
<meta property="og:url" content="https://yoursite.com/page" />
<meta property="og:type" content="website" />
Twitter uses its own twitter:card variants, but it also falls back to OpenGraph tags, so a single implementation covers both.
Setting up react-helmet
For React SPAs, injecting <head> tags from inside components requires a library. react-helmet-async is the modern, concurrent-safe version:
npm install react-helmet-async
Wrap your app with the provider at the root:
// main.tsx
import { HelmetProvider } from 'react-helmet-async';
root.render(
<HelmetProvider>
<App />
</HelmetProvider>
);
The OpenGraph component
Create a reusable component that accepts the metadata as props and injects the tags:
// components/SEO.tsx
import { Helmet } from 'react-helmet-async';
interface SEOProps {
title: string;
description: string;
url: string;
image: string;
siteName?: string;
type?: 'website' | 'article' | 'profile';
}
export function SEO({
title,
description,
url,
image,
siteName = 'My Site',
type = 'website',
}: SEOProps) {
return (
<Helmet>
<title>{title}</title>
<meta name="description" content={description} />
{/* OpenGraph */}
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={url} />
<meta property="og:image" content={image} />
<meta property="og:type" content={type} />
<meta property="og:site_name" content={siteName} />
{/* Twitter Card */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={image} />
</Helmet>
);
}
Using it on a page
Drop <SEO> at the top of any page component:
// pages/BlogPost.tsx
import { SEO } from '../components/SEO';
export function BlogPost({ post }: { post: Post }) {
return (
<>
<SEO
title={post.title}
description={post.excerpt}
url={`https://yoursite.com/blog/${post.slug}`}
image={post.coverImage ?? 'https://yoursite.com/default-cover.png'}
type="article"
/>
<article>
<h1>{post.title}</h1>
{/* content */}
</article>
</>
);
}
Each page sets its own metadata independently. No global state, no prop drilling.
Image requirements
The og:image URL must be:
- Absolute — relative paths don’t work in social crawlers
- Publicly accessible — no auth, no localhost
- At least 1200×630px — the recommended size for
summary_large_image - Under 5MB — Twitter’s hard limit
For a quick test, use the Twitter Card Validator or Open Graph Debugger after deploying.
Static sites: skip the library
If you’re using Astro, Next.js App Router, or any framework that renders HTML server-side, you don’t need react-helmet at all. Inject tags directly in your layout:
---
// layouts/BaseLayout.astro
const { title, description, image } = Astro.props;
---
<head>
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={image} />
</head>
Server-rendered tags are visible to crawlers on the first request, which is the more reliable approach for SEO.
Key takeaways
- OpenGraph controls how pages appear in social previews on all major platforms
react-helmet-asyncis the correct library for React SPAs — not the olderreact-helmet- Build one reusable
<SEO>component and drop it on every page - Images must be absolute URLs, publicly accessible, and at minimum 1200×630px
- For server-rendered frameworks, inject tags at the layout level — no library needed
Social sharing is often the first impression someone has of your content. A few meta tags make a significant difference in click-through rate.