PWA Open Graph Tags: How to Add OG Meta to Progressive Web Apps
How to add og:title, og:image, og:description, and Twitter card meta tags to Progressive Web Apps — and why PWAs need special handling for social crawlers that don't execute JavaScript.
Why PWAs have OG tag challenges
Progressive Web Apps are typically built as SPAs (Single Page Applications) where HTML is rendered by JavaScript at runtime. Social media crawlers — Twitterbot, LinkedInbot, Facebookbot, Slackbot — don't run JavaScript, so they only see your initial HTML shell, which usually has no meaningful OG tags.
Additionally, PWAs use a manifest.json for app installation metadata, but the manifest.json is not read by social crawlers for link previews. OG tags must live in the <head> of your HTML.
Static OG tags in index.html
The simplest approach for a PWA is to add OG tags directly to your public/index.html. These are served on every route and visible to crawlers before JavaScript runs:
<!-- public/index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Your PWA Name</title> <meta name="description" content="Your app description — what users get." /> <!-- OG tags (seen by crawlers before JS runs) --> <meta property="og:title" content="Your PWA Name" /> <meta property="og:description" content="Your app description" /> <meta property="og:image" content="https://yourdomain.com/og-image.png" /> <meta property="og:url" content="https://yourdomain.com" /> <meta property="og:type" content="website" /> <!-- Twitter Card --> <meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:title" content="Your PWA Name" /> <meta name="twitter:description" content="Your app description" /> <meta name="twitter:image" content="https://yourdomain.com/og-image.png" /> <!-- Canonical --> <link rel="canonical" href="https://yourdomain.com" /> <!-- PWA manifest (separate from OG — for app installation) --> <link rel="manifest" href="/manifest.json" /> <meta name="theme-color" content="#0a0a0a" /> </head> <body> <div id="root"></div> </body> </html>
These static tags become the social preview for every URL of your PWA. For most apps this is acceptable for the homepage share case. For per-route previews, see SSR below.
Per-route OG with SSR (Next.js PWA)
If you need unique OG tags per route (e.g., for product pages or blog posts), use a framework with server-side rendering. Next.js with the App Router is the most common choice:
// next.config.ts — enable PWA features
import withPWA from 'next-pwa';
/** @type {import('next').NextConfig} */
const nextConfig = withPWA({
dest: 'public',
register: true,
skipWaiting: true,
disable: process.env.NODE_ENV === 'development',
})({});
export default nextConfig;// app/products/[slug]/page.tsx
import type { Metadata } from 'next';
export async function generateMetadata({
params,
}: {
params: { slug: string };
}): Promise<Metadata> {
const product = await fetchProduct(params.slug);
return {
title: product.name,
description: product.description,
openGraph: {
title: product.name,
description: product.description,
images: [
{
url: product.image,
width: 1200,
height: 630,
alt: product.name,
},
],
url: `https://yourdomain.com/products/${params.slug}`,
type: 'website',
},
twitter: {
card: 'summary_large_image',
title: product.name,
description: product.description,
images: [product.image],
},
alternates: {
canonical: `/products/${params.slug}`,
},
};
}With Next.js SSR, each route gets unique OG tags rendered server-side — visible to crawlers without requiring JavaScript execution.
Web App Manifest vs OG tags
A common PWA mistake is thinking the manifest.json handles social previews. It doesn't. The manifest covers:
- App name and icons on the home screen
- Splash screen and theme color
- Display mode (fullscreen, standalone)
Social crawlers use OG meta tags only. You need both:
<!-- PWA manifest (for installability) --> <link rel="manifest" href="/manifest.json" /> <!-- OG tags (for social sharing) --> <meta property="og:title" content="Your App" /> <meta property="og:image" content="https://yourdomain.com/og.png" /> <meta property="og:description" content="Your app description" />
Prerendering with Rendertron or Prerender.io
If your PWA is built without SSR (e.g., vanilla React or Vue), you can use a prerendering service to serve pre-rendered HTML to social crawlers:
- Prerender.io — cloud service that crawls your SPA and caches snapshots; returns pre-rendered HTML when bot user-agents are detected.
- Rendertron — self-hosted headless Chrome prerenderer; set up at a proxy or as a Cloudflare Worker.
- Vercel / Netlify Edge Functions — detect bot user agents at the edge and return a pre-rendered version from a build-time static generation step.
Verify your PWA OG tags
Once your PWA is deployed, paste any URL into OGFixer to preview how your link appears on Twitter, LinkedIn, Slack, and Discord — and catch missing or static-only fallbacks before they go live.