📦 deps(thirdparty): update snapshots
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
---
|
||||
name: social-metadata-hardening
|
||||
description: "Fix social sharing previews so URLs render as rich cards on Facebook, LinkedIn, X/Twitter, WhatsApp, Telegram, Slack, and Discord. Covers OG tags, Twitter cards, absolute image URLs, and metadata debugging."
|
||||
category: seo
|
||||
risk: safe
|
||||
source: self
|
||||
source_type: self
|
||||
date_added: "2026-05-31"
|
||||
author: Whoisabhishekadhikari
|
||||
tags: [seo, open-graph, twitter-card, social-sharing, og-image, nextjs, metadata]
|
||||
tools: [claude, cursor, gemini, claude-code]
|
||||
version: 1.0.0
|
||||
---
|
||||
|
||||
# Social Metadata Hardening Skill
|
||||
|
||||
Fix social sharing so every important URL unfurls as a rich card across all platforms.
|
||||
|
||||
---
|
||||
|
||||
## When to Use
|
||||
|
||||
- Use when shared links show missing, stale, cropped, or incorrect previews on social and chat platforms.
|
||||
- Use when auditing Open Graph, Twitter/X card, image URL, alt text, or `metadataBase` coverage in a web app.
|
||||
- Use before launch when every public page needs predictable rich previews across LinkedIn, X, Facebook, WhatsApp, Slack, Discord, and Telegram.
|
||||
|
||||
---
|
||||
|
||||
## Why Previews Break
|
||||
|
||||
| Problem | Root Cause |
|
||||
|---------|-----------|
|
||||
| No preview at all | Missing og:title, og:description, or og:image |
|
||||
| Broken image | Relative URL (must be absolute) |
|
||||
| Wrong image size | Image not 1200×630px (OG standard) |
|
||||
| Plain text card | Twitter card type missing or set to `summary` |
|
||||
| Stale preview | Platform caching old metadata |
|
||||
| Metadata missing on crawl | Tags added by client-side JS (crawlers don't run JS) |
|
||||
|
||||
---
|
||||
|
||||
## The Gold Standard Metadata Block
|
||||
|
||||
Every shareable page needs ALL of these in static HTML:
|
||||
|
||||
```js
|
||||
// Next.js App Router — lib/socialMetadata.js
|
||||
export function buildSocialMetadata({
|
||||
title,
|
||||
description,
|
||||
path, // '/blog/my-post'
|
||||
image, // '/images/og/my-post.jpg' or full URL
|
||||
imageAlt,
|
||||
imageWidth = 1200,
|
||||
imageHeight = 630,
|
||||
}) {
|
||||
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'https://www.yourdomain.com';
|
||||
|
||||
// Always produce an absolute URL
|
||||
const imageUrl = image?.startsWith('http') ? image : `${baseUrl}${image}`;
|
||||
const pageUrl = `${baseUrl}${path}`;
|
||||
|
||||
// Detect MIME type from extension
|
||||
const ext = imageUrl.split('.').pop().toLowerCase();
|
||||
const mimeMap = { jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png', webp: 'image/webp' };
|
||||
const imageType = mimeMap[ext] || 'image/jpeg';
|
||||
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
alternates: { canonical: pageUrl },
|
||||
openGraph: {
|
||||
title,
|
||||
description,
|
||||
url: pageUrl,
|
||||
type: 'website', // use 'article' for blog posts
|
||||
images: [{
|
||||
url: imageUrl,
|
||||
secureUrl: imageUrl, // explicit HTTPS version
|
||||
width: imageWidth,
|
||||
height: imageHeight,
|
||||
alt: imageAlt || title,
|
||||
type: imageType,
|
||||
}],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image', // NOT 'summary' — that shows a tiny image
|
||||
title,
|
||||
description,
|
||||
images: [imageUrl],
|
||||
},
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Applying the Helper
|
||||
|
||||
### Static page
|
||||
```js
|
||||
// app/about/page.js
|
||||
import { buildSocialMetadata } from '@/lib/socialMetadata';
|
||||
|
||||
export const metadata = buildSocialMetadata({
|
||||
title: 'About Us | My Site',
|
||||
description: 'Learn about our team and mission.',
|
||||
path: '/about',
|
||||
image: '/images/og/about.jpg',
|
||||
imageAlt: 'The My Site team',
|
||||
});
|
||||
```
|
||||
|
||||
### Dynamic page (blog post, tool page)
|
||||
```js
|
||||
// app/blog/[slug]/page.js
|
||||
import { buildSocialMetadata } from '@/lib/socialMetadata';
|
||||
|
||||
export async function generateMetadata({ params }) {
|
||||
const post = await getPost(params.slug);
|
||||
return buildSocialMetadata({
|
||||
title: `${post.title} | My Blog`,
|
||||
description: post.excerpt,
|
||||
path: `/blog/${params.slug}`,
|
||||
image: post.ogImage || '/images/og/default.jpg',
|
||||
imageAlt: post.title,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Homepage (app/layout.js or app/page.js)
|
||||
```js
|
||||
export const metadata = {
|
||||
metadataBase: new URL('https://www.yourdomain.com'), // REQUIRED for absolute URLs
|
||||
...buildSocialMetadata({
|
||||
title: 'My Site — Tagline Here',
|
||||
description: 'Site-wide description.',
|
||||
path: '/',
|
||||
image: '/images/og/home.jpg',
|
||||
}),
|
||||
};
|
||||
```
|
||||
|
||||
> ⚠️ **`metadataBase` is critical.** Without it, Next.js generates relative OG image URLs that every platform rejects.
|
||||
|
||||
---
|
||||
|
||||
## OG Image Checklist
|
||||
|
||||
Good OG images:
|
||||
- **1200 × 630px** (2:1 ratio — works on all platforms)
|
||||
- **Under 8MB** (Facebook limit)
|
||||
- Served over **HTTPS**
|
||||
- File name has **no spaces** (use hyphens)
|
||||
- Format: **JPEG or PNG** (WebP works on most but not all crawlers)
|
||||
- **Accessible via GET** with no authentication
|
||||
|
||||
```bash
|
||||
# Verify your OG image is reachable and correct size
|
||||
curl -sI https://www.yourdomain.com/images/og/home.jpg | grep -i "content-type\|content-length\|status"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Platform-Specific Notes
|
||||
|
||||
### Facebook / Meta
|
||||
- Caches aggressively — use the [Sharing Debugger](https://developers.facebook.com/tools/debug/) to force recrawl
|
||||
- Minimum image: 200×200px (but use 1200×630 for quality)
|
||||
- Needs: `og:title`, `og:description`, `og:image`, `og:url`
|
||||
|
||||
### X / Twitter
|
||||
- Use `twitter:card = summary_large_image` for full-width images
|
||||
- `twitter:image` must be an absolute URL
|
||||
- Use the [Card Validator](https://cards-dev.twitter.com/validator) to test
|
||||
|
||||
### LinkedIn
|
||||
- Caches hard — use [Post Inspector](https://www.linkedin.com/post-inspector/) to refresh
|
||||
- Respects `og:` tags; ignores `twitter:` tags
|
||||
- Image must be ≥1.91:1 aspect ratio
|
||||
|
||||
### WhatsApp / Telegram
|
||||
- Read OG tags on first share; cache can last hours
|
||||
- Re-share after a few hours for the cache to clear naturally
|
||||
|
||||
### Slack / Discord
|
||||
- Both use OG tags; both cache
|
||||
- Discord also supports `og:type = article` for richer embeds
|
||||
|
||||
---
|
||||
|
||||
## Debugging Social Previews
|
||||
|
||||
### 1. Check raw HTML for tags
|
||||
```bash
|
||||
curl -s https://www.yourdomain.com/blog/my-post | grep -i "og:\|twitter:"
|
||||
```
|
||||
If tags don't appear → they're being added by JavaScript (not crawlable). Fix: move to `export const metadata` or `generateMetadata`.
|
||||
|
||||
### 2. Validate with platform tools
|
||||
| Platform | Tool |
|
||||
|----------|------|
|
||||
| Facebook | https://developers.facebook.com/tools/debug/ |
|
||||
| LinkedIn | https://www.linkedin.com/post-inspector/ |
|
||||
| Twitter/X | https://cards-dev.twitter.com/validator |
|
||||
| General | https://metatags.io |
|
||||
|
||||
### 3. Force cache refresh
|
||||
After deploying fixes, paste the URL into each platform's debugger and click "Fetch new scrape information" (or equivalent).
|
||||
|
||||
---
|
||||
|
||||
## Social Metadata Checklist
|
||||
|
||||
- [ ] `metadataBase` set in root layout
|
||||
- [ ] All shareable pages use shared `buildSocialMetadata` helper
|
||||
- [ ] OG image URLs are absolute (start with `https://`)
|
||||
- [ ] `secureUrl` set equal to `url` in OG image block
|
||||
- [ ] Image is 1200×630px, under 8MB, HTTPS
|
||||
- [ ] `twitter:card` is `summary_large_image` (not `summary`)
|
||||
- [ ] Image alt text present
|
||||
- [ ] Tags visible in raw HTML (not JavaScript-rendered)
|
||||
- [ ] All platform debuggers show correct preview
|
||||
- [ ] Cache refreshed on all platforms after deployment
|
||||
|
||||
## Limitations
|
||||
|
||||
- Cannot force immediate cache refresh on every social platform; some previews may remain stale after a correct fix.
|
||||
- Requires deployed, publicly reachable URLs for reliable validation with platform debuggers.
|
||||
- Does not replace brand, accessibility, or legal review of image text, alt text, and preview copy.
|
||||
Reference in New Issue
Block a user