Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
menu
  1. Home
  2. /Web Development
  3. /Nextjs
  4. /Opengraph Social Cards
Your Progress0%
0 of 70 completed

NextJS Topics

Getting Started

  • What is Next.js?
  • Next.js vs React - Key Differences
  • Creating Your First Next.js 15 Project
  • Next.js Project Structure Explained
  • App Router vs Pages Router

Routing Fundamentals

  • File-Based Routing Basics
  • Creating Pages with page.tsx
  • Dynamic Routes and Route Parameters
  • Catch-All and Optional Catch-All Routes
  • Route Groups for Organization
  • Parallel Routes
  • Intercepting Routes

Layouts and Pages

  • Understanding Layouts
  • Root Layout and Global Configuration
  • Nested Layouts
  • Templates vs Layouts
  • Loading States with loading.tsx
  • Error Handling with error.tsx

Server and Client Components

  • Understanding Server Components
  • Client Components with 'use client'
  • When to Use Server vs Client Components
  • Component Composition Patterns
  • Passing Props Between Server and Client
  • Server Component Patterns and Best Practices

Data Fetching

  • Fetching Data in Server Components
  • Parallel and Sequential Data Fetching
  • Data Fetching Patterns and Strategies
  • Caching and Revalidation
  • Revalidate and Cache Tags
  • Handling Loading and Error States in Data Fetching

Navigation and Links

  • Link Component Basics
  • useRouter Hook for Programmatic Navigation
  • usePathname and useSearchParams Hooks
  • Active Links and Navigation States
  • Redirects and Navigation Guards

Styling in Next.js

  • CSS Modules in Next.js
  • Tailwind CSS Setup and Configuration
  • Global Styles and CSS Variables
  • Next.js Font Optimization

Images and Media

  • Next.js Image Component Basics
  • Image Optimization and Best Practices
  • Working with Static Assets

Forms and Data Mutations

  • Understanding Server Actions
  • Form Handling with Server Actions
  • Form Validation and Error Handling
  • useFormStatus and useFormState Hooks
  • Revalidating Data After Mutations
  • Optimistic Updates

Metadata and SEO

  • Static Metadata Configuration
  • Dynamic Metadata Generation
  • Open Graph and Social Media Cards
  • Sitemap and Robots.txt

API Routes and Route Handlers

  • Route Handlers Introduction
  • GET and POST Request Handlers
  • Dynamic API Routes
  • Request and Response Objects
  • API Error Handling and Status Codes

Middleware and Advanced Features

  • Introduction to Middleware
  • Authentication with Middleware
  • Environment Variables and Configuration
  • Streaming and Suspense
  • Not Found and Global Error Pages

Deployment and Optimization

  • Understanding Static and Dynamic Rendering
  • generateStaticParams for Static Generation
  • Build and Production Optimization
  • Deploying to Vercel
  • Alternative Deployment Options
  • Performance Monitoring and Analytics

Real-World Projects

  • Project 1: E-commerce Product Catalog
  • Project 2: Dashboard with Authentication

Open Graph and Social Media Cards

Creating stunning social media previews

When someone shares your content on social media, a preview card appears with an image, title, and description. These cards dramatically increase click-through rates. Next.js makes it easy to create dynamic Open Graph images and optimize social media previews with the ImageResponse API. Build cards that stand out and drive engagement!

Why Social Media Cards Matter

Impact of Good Social Cards

  • 3x higher click-through rates compared to plain links
  • Professional appearance builds trust and credibility
  • Brand consistency across all social platforms
  • Better engagement on Facebook, Twitter, LinkedIn
  • Preview control instead of random images

How Social Cards Work

  1. User shares your URL on social media
  2. Platform fetches your page's Open Graph metadata
  3. Platform displays card with og:image, og:title, og:description
  4. Users see rich preview instead of plain link
  5. More clicks and better engagement

Open Graph Metadata Basics

Essential Open Graph Tags

app/blog/[slug]/page.tsx
import { Metadata } from 'next';

export const metadata: Metadata = {
  // Regular metadata
  title: 'My Blog Post',
  description: 'An amazing blog post about Next.js',
  
  // Open Graph metadata
  openGraph: {
    title: 'My Blog Post',  // Can differ from page title
    description: 'An amazing blog post about Next.js',
    url: 'https://myblog.com/blog/my-post',
    siteName: 'My Blog',
    locale: 'en_US',
    type: 'article',
    
    // Images array
    images: [
      {
        url: 'https://myblog.com/og-images/my-post.jpg',
        width: 1200,
        height: 630,
        alt: 'My Blog Post Cover',
      },
      // Fallback image
      {
        url: 'https://myblog.com/og-images/default.jpg',
        width: 1200,
        height: 630,
        alt: 'My Blog Default Image',
      },
    ],
    
    // Article-specific (optional)
    publishedTime: '2024-01-15T00:00:00.000Z',
    modifiedTime: '2024-01-16T00:00:00.000Z',
    authors: ['John Doe'],
    tags: ['Next.js', 'React', 'Web Development'],
  },
};

// Generates:
// <meta property="og:title" content="My Blog Post" />
// <meta property="og:description" content="..." />
// <meta property="og:url" content="..." />
// <meta property="og:image" content="..." />
// <meta property="og:image:width" content="1200" />
// <meta property="og:image:height" content="630" />

// ✅ 1200x630 recommended size
// ✅ Multiple images (first is primary)
// ✅ Article-specific metadata
// ✅ Type: 'article' for blog posts

Open Graph Content Types

TYPESCRIPT
// Different types for different content
export const metadata: Metadata = {
  openGraph: {
    // Website (default)
    type: 'website',
    
    // Article (blog posts)
    type: 'article',
    publishedTime: '2024-01-15T00:00:00.000Z',
    authors: ['Author Name'],
    
    // Profile (user pages)
    type: 'profile',
    firstName: 'John',
    lastName: 'Doe',
    username: 'johndoe',
    
    // Video
    type: 'video.other',
    video: 'https://example.com/video.mp4',
    
    // Music
    type: 'music.song',
    
    // Book
    type: 'book',
    authors: ['Author Name'],
    isbn: '978-3-16-148410-0',
  },
};

// ✅ Match type to content
// ✅ Rich snippets on platforms
// ✅ Type-specific properties

Twitter Cards

Twitter Card Types

TYPESCRIPT
export const metadata: Metadata = {
  twitter: {
    // Card types:
    card: 'summary_large_image',  // Large image (recommended)
    // card: 'summary',           // Small thumbnail
    // card: 'player',            // Video/audio player
    // card: 'app',               // Mobile app
    
    title: 'My Blog Post',
    description: 'An amazing blog post',
    creator: '@myhandle',      // Content creator
    site: '@mysitehandle',     // Website handle
    
    images: ['https://myblog.com/twitter-image.jpg'],
    
    // Player card specific
    // player: 'https://example.com/player',
    // playerWidth: 1280,
    // playerHeight: 720,
  },
};

// Generates:
// <meta name="twitter:card" content="summary_large_image" />
// <meta name="twitter:title" content="My Blog Post" />
// <meta name="twitter:description" content="..." />
// <meta name="twitter:creator" content="@myhandle" />
// <meta name="twitter:image" content="..." />

// ✅ summary_large_image for blog posts
// ✅ Include creator handle
// ✅ Can differ from Open Graph

Combining Open Graph and Twitter

TYPESCRIPT
export const metadata: Metadata = {
  title: 'My Blog Post',
  description: 'Blog post description',
  
  // Open Graph (Facebook, LinkedIn, etc.)
  openGraph: {
    title: 'My Amazing Blog Post!',  // More engaging for social
    description: 'Discover how to build amazing things with Next.js',
    type: 'article',
    images: [
      {
        url: 'https://myblog.com/og-image.jpg',
        width: 1200,
        height: 630,
      },
    ],
  },
  
  // Twitter (uses Open Graph as fallback)
  twitter: {
    card: 'summary_large_image',
    title: 'My Amazing Blog Post! 🚀',  // Can add emoji for Twitter
    description: 'Discover how to build amazing things with Next.js',
    creator: '@myhandle',
    images: ['https://myblog.com/twitter-image.jpg'],  // Twitter-specific image
  },
};

// ✅ OG title can differ from page title
// ✅ Twitter can differ from OG
// ✅ Platform-specific optimization
// ✅ Twitter uses OG as fallback if not specified

Dynamic OG Image Generation

Static OG Image File

app/opengraph-image.tsx
import { ImageResponse } from 'next/og';

// Image metadata
export const alt = 'My Site';
export const size = {
  width: 1200,
  height: 630,
};
export const contentType = 'image/png';

// Image generation
export default async function Image() {
  return new ImageResponse(
    (
      // JSX/HTML-like syntax
      <div
        style={{
          fontSize: 128,
          background: 'linear-gradient(to bottom right, #1e40af, #7c3aed)',
          width: '100%',
          height: '100%',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          color: 'white',
        }}
      >
        My Site
      </div>
    ),
    {
      ...size,
    }
  );
}

// ✅ File-based OG image generation
// ✅ HTML/CSS-like syntax
// ✅ Automatic at /opengraph-image
// ✅ No external image needed

Dynamic OG Image with Route Params

app/blog/[slug]/opengraph-image.tsx
import { ImageResponse } from 'next/og';

export const alt = 'Blog Post';
export const size = {
  width: 1200,
  height: 630,
};
export const contentType = 'image/png';

export default async function Image({ 
  params 
}: { 
  params: { slug: string } 
}) {
  // Fetch post data
  const post = await fetch(`https://api.example.com/posts/${params.slug}`)
    .then(res => res.json());

  return new ImageResponse(
    (
      <div
        style={{
          fontSize: 60,
          background: 'white',
          width: '100%',
          height: '100%',
          display: 'flex',
          flexDirection: 'column',
          alignItems: 'center',
          justifyContent: 'center',
          padding: '40px 80px',
        }}
      >
        {/* Title */}
        <div
          style={{
            fontSize: 72,
            fontWeight: 'bold',
            marginBottom: 20,
            color: '#1e40af',
            textAlign: 'center',
          }}
        >
          {post.title}
        </div>
        
        {/* Author */}
        <div
          style={{
            fontSize: 36,
            color: '#6b7280',
          }}
        >
          By {post.author}
        </div>
        
        {/* Site name */}
        <div
          style={{
            fontSize: 30,
            color: '#9ca3af',
            marginTop: 40,
          }}
        >
          myblog.com
        </div>
      </div>
    ),
    {
      ...size,
    }
  );
}

// ✅ Dynamic content from database
// ✅ Post title in image
// ✅ Author name
// ✅ Automatically generated per post

Advanced OG Image with Custom Fonts

app/blog/[slug]/opengraph-image.tsx
import { ImageResponse } from 'next/og';

export default async function Image({ 
  params 
}: { 
  params: { slug: string } 
}) {
  const post = await getPost(params.slug);
  
  // Load custom font
  const interSemiBold = fetch(
    new URL('./Inter-SemiBold.ttf', import.meta.url)
  ).then((res) => res.arrayBuffer());

  return new ImageResponse(
    (
      <div
        style={{
          height: '100%',
          width: '100%',
          display: 'flex',
          flexDirection: 'column',
          alignItems: 'center',
          justifyContent: 'center',
          backgroundColor: '#fff',
          backgroundImage: 'linear-gradient(to bottom right, #e0e7ff, #fce7f3)',
        }}
      >
        {/* Content container */}
        <div
          style={{
            display: 'flex',
            flexDirection: 'column',
            padding: '60px 80px',
            maxWidth: '1000px',
          }}
        >
          {/* Category badge */}
          <div
            style={{
              display: 'flex',
              fontSize: 28,
              fontWeight: 'bold',
              color: '#7c3aed',
              marginBottom: 20,
              textTransform: 'uppercase',
              letterSpacing: '0.1em',
            }}
          >
            {post.category}
          </div>
          
          {/* Title */}
          <div
            style={{
              fontSize: 80,
              fontWeight: 'bold',
              color: '#1e293b',
              lineHeight: 1.1,
              marginBottom: 30,
            }}
          >
            {post.title}
          </div>
          
          {/* Metadata */}
          <div
            style={{
              display: 'flex',
              fontSize: 32,
              color: '#64748b',
              alignItems: 'center',
              gap: 20,
            }}
          >
            <div>{post.author}</div>
            <div>•</div>
            <div>{new Date(post.publishedAt).toLocaleDateString()}</div>
          </div>
        </div>
        
        {/* Logo corner */}
        <div
          style={{
            position: 'absolute',
            bottom: 40,
            right: 60,
            fontSize: 36,
            fontWeight: 'bold',
            color: '#7c3aed',
          }}
        >
          myblog.com
        </div>
      </div>
    ),
    {
      ...size,
      fonts: [
        {
          name: 'Inter',
          data: await interSemiBold,
          style: 'normal',
          weight: 600,
        },
      ],
    }
  );
}

// ✅ Custom fonts
// ✅ Complex layouts
// ✅ Gradient backgrounds
// ✅ Category, date, author
// ✅ Professional design

Reusable OG Image Templates

Template Component

app/lib/og-templates.tsx
interface BlogPostOGProps {
  title: string;
  author: string;
  category: string;
  publishedAt: string;
}

export function BlogPostOGTemplate({
  title,
  author,
  category,
  publishedAt,
}: BlogPostOGProps) {
  return (
    <div
      style={{
        height: '100%',
        width: '100%',
        display: 'flex',
        flexDirection: 'column',
        backgroundColor: '#fff',
        backgroundImage: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
        padding: '60px 80px',
      }}
    >
      {/* Category */}
      <div
        style={{
          fontSize: 32,
          fontWeight: 'bold',
          color: 'rgba(255, 255, 255, 0.9)',
          marginBottom: 20,
          textTransform: 'uppercase',
        }}
      >
        {category}
      </div>
      
      {/* Title */}
      <div
        style={{
          fontSize: 72,
          fontWeight: 'bold',
          color: '#fff',
          lineHeight: 1.2,
          marginBottom: 40,
          textShadow: '0 2px 10px rgba(0,0,0,0.2)',
        }}
      >
        {title}
      </div>
      
      {/* Footer */}
      <div
        style={{
          display: 'flex',
          marginTop: 'auto',
          fontSize: 28,
          color: 'rgba(255, 255, 255, 0.8)',
          alignItems: 'center',
          gap: 15,
        }}
      >
        <div>{author}</div>
        <div>•</div>
        <div>{new Date(publishedAt).toLocaleDateString()}</div>
      </div>
    </div>
  );
}

// ✅ Reusable template
// ✅ Type-safe props
// ✅ Consistent design
// ✅ Easy to maintain

Using Template in Route

app/blog/[slug]/opengraph-image.tsx
import { ImageResponse } from 'next/og';
import { BlogPostOGTemplate } from '@/app/lib/og-templates';

export const size = {
  width: 1200,
  height: 630,
};

export default async function Image({ 
  params 
}: { 
  params: { slug: string } 
}) {
  const post = await getPost(params.slug);

  return new ImageResponse(
    (
      <BlogPostOGTemplate
        title={post.title}
        author={post.author}
        category={post.category}
        publishedAt={post.publishedAt}
      />
    ),
    {
      ...size,
    }
  );
}

// ✅ Clean separation
// ✅ Reuse across routes
// ✅ Consistent branding
// ✅ Easy updates

Complete Social Card Examples

Example 1: E-commerce Product Card

app/products/[id]/page.tsx
import { Metadata } from 'next';

export async function generateMetadata({
  params,
}: {
  params: { id: string };
}): Promise<Metadata> {
  const product = await getProduct(params.id);

  return {
    title: `${product.name} - ${product.brand}`,
    description: product.description,
    
    openGraph: {
      title: product.name,
      description: product.description,
      type: 'product',
      url: `https://mystore.com/products/${params.id}`,
      siteName: 'My Store',
      
      images: [
        {
          url: product.images[0],
          width: 1200,
          height: 630,
          alt: product.name,
        },
      ],
      
      // Product-specific metadata
      'product:price:amount': product.price.toString(),
      'product:price:currency': 'USD',
      'product:availability': product.inStock ? 'in stock' : 'out of stock',
      'product:brand': product.brand,
      'product:category': product.category,
    },
    
    twitter: {
      card: 'summary_large_image',
      title: `${product.name} - Only $${product.price}`,
      description: product.description,
      images: [product.images[0]],
    },
  };
}

// ✅ Product-specific Open Graph
// ✅ Price and availability
// ✅ Engaging Twitter title
// ✅ Primary product image
app/products/[id]/opengraph-image.tsx
import { ImageResponse } from 'next/og';

export const size = { width: 1200, height: 630 };

export default async function Image({ 
  params 
}: { 
  params: { id: string } 
}) {
  const product = await getProduct(params.id);

  return new ImageResponse(
    (
      <div
        style={{
          display: 'flex',
          width: '100%',
          height: '100%',
          backgroundColor: '#fff',
        }}
      >
        {/* Product image side */}
        <div
          style={{
            display: 'flex',
            width: '50%',
            height: '100%',
            backgroundColor: '#f3f4f6',
            alignItems: 'center',
            justifyContent: 'center',
          }}
        >
          {/* Product image would go here */}
          <div
            style={{
              fontSize: 120,
            }}
          >
            📦
          </div>
        </div>
        
        {/* Product info side */}
        <div
          style={{
            display: 'flex',
            flexDirection: 'column',
            width: '50%',
            padding: '60px',
            justifyContent: 'center',
          }}
        >
          {/* Brand */}
          <div
            style={{
              fontSize: 32,
              color: '#6b7280',
              marginBottom: 20,
            }}
          >
            {product.brand}
          </div>
          
          {/* Product name */}
          <div
            style={{
              fontSize: 56,
              fontWeight: 'bold',
              color: '#1f2937',
              lineHeight: 1.2,
              marginBottom: 30,
            }}
          >
            {product.name}
          </div>
          
          {/* Price */}
          <div
            style={{
              fontSize: 64,
              fontWeight: 'bold',
              color: '#10b981',
            }}
          >
            ${product.price}
          </div>
          
          {/* Stock status */}
          <div
            style={{
              fontSize: 28,
              color: product.inStock ? '#10b981' : '#ef4444',
              marginTop: 20,
            }}
          >
            {product.inStock ? '✓ In Stock' : '✗ Out of Stock'}
          </div>
        </div>
      </div>
    ),
    { ...size }
  );
}

// ✅ Split layout design
// ✅ Product image placeholder
// ✅ Price highlighted
// ✅ Stock status visible

Example 2: Event Card

app/events/[id]/opengraph-image.tsx
import { ImageResponse } from 'next/og';

export default async function Image({ 
  params 
}: { 
  params: { id: string } 
}) {
  const event = await getEvent(params.id);
  const eventDate = new Date(event.date);

  return new ImageResponse(
    (
      <div
        style={{
          display: 'flex',
          width: '100%',
          height: '100%',
          backgroundColor: '#0f172a',
          color: '#fff',
          position: 'relative',
          overflow: 'hidden',
        }}
      >
        {/* Background pattern */}
        <div
          style={{
            position: 'absolute',
            width: '100%',
            height: '100%',
            backgroundImage: 'radial-gradient(circle, #1e293b 1px, transparent 1px)',
            backgroundSize: '50px 50px',
            opacity: 0.3,
          }}
        />
        
        {/* Content */}
        <div
          style={{
            display: 'flex',
            flexDirection: 'column',
            padding: '80px',
            position: 'relative',
          }}
        >
          {/* Date badge */}
          <div
            style={{
              display: 'flex',
              flexDirection: 'column',
              backgroundColor: '#7c3aed',
              padding: '30px',
              borderRadius: '20px',
              alignItems: 'center',
              width: '200px',
              marginBottom: 40,
            }}
          >
            <div style={{ fontSize: 72, fontWeight: 'bold' }}>
              {eventDate.getDate()}
            </div>
            <div style={{ fontSize: 36 }}>
              {eventDate.toLocaleDateString('en-US', { month: 'short' })}
            </div>
          </div>
          
          {/* Event name */}
          <div
            style={{
              fontSize: 80,
              fontWeight: 'bold',
              lineHeight: 1.1,
              marginBottom: 30,
            }}
          >
            {event.name}
          </div>
          
          {/* Location & Time */}
          <div
            style={{
              display: 'flex',
              flexDirection: 'column',
              fontSize: 36,
              color: '#94a3b8',
              gap: 15,
            }}
          >
            <div>📍 {event.location}</div>
            <div>🕐 {event.time}</div>
          </div>
        </div>
      </div>
    ),
    { width: 1200, height: 630 }
  );
}

// ✅ Event-specific design
// ✅ Date prominently displayed
// ✅ Location and time
// ✅ Dark, modern theme

Open Graph Image Structure

Organization of OG image generators

appImportant

Select a file or folder to see details

Testing and Debugging Social Cards

Testing Tools

Essential Testing Tools

  • Facebook Sharing Debugger: https://developers.facebook.com/tools/debug/
  • Twitter Card Validator: https://cards-dev.twitter.com/validator
  • LinkedIn Post Inspector: https://www.linkedin.com/post-inspector/
  • OpenGraph.xyz: https://www.opengraph.xyz/
  • Meta Tags: https://metatags.io/

Common Issues and Solutions

TYPESCRIPT
// ❌ PROBLEM: Image not showing
// Solution 1: Check image size (1200x630 recommended)
openGraph: {
  images: [
    {
      url: 'https://example.com/og-image.jpg',
      width: 1200,  // ✅ Specify dimensions
      height: 630,
    },
  ],
}

// ❌ PROBLEM: Old image cached
// Solution 2: Add version parameter to URL
openGraph: {
  images: ['https://example.com/og-image.jpg?v=2'],
}

// ❌ PROBLEM: Wrong image shown
// Solution 3: Use absolute URLs
openGraph: {
  images: ['https://example.com/og-image.jpg'],  // ✅ Absolute
  // NOT: '/og-image.jpg'  // ❌ Relative won't work
}

// ❌ PROBLEM: Different platforms show different images
// Solution 4: Specify platform-specific images
openGraph: {
  images: ['https://example.com/og-image.jpg'],
}
twitter: {
  images: ['https://example.com/twitter-image.jpg'],  // Twitter-specific
}

// ✅ Test on all major platforms
// ✅ Clear cache in testing tools
// ✅ Use absolute URLs
// ✅ Specify dimensions

Social Card Best Practices

1. Use Correct Image Sizes

TYPESCRIPT
// ✅ GOOD: Recommended sizes
// Open Graph: 1200x630 (1.91:1 ratio)
// Twitter summary_large_image: 1200x630
// Twitter summary: 120x120 (1:1 ratio)

export const metadata: Metadata = {
  openGraph: {
    images: [
      {
        url: 'https://example.com/og-image.jpg',
        width: 1200,
        height: 630,
        alt: 'Descriptive alt text',
      },
    ],
  },
};

// ❌ BAD: Wrong sizes
// 800x600 - Too small
// 2000x2000 - Wrong ratio, will be cropped

2. Always Include Alt Text

TYPESCRIPT
// ✅ GOOD: Descriptive alt text
openGraph: {
  images: [
    {
      url: 'https://example.com/post-image.jpg',
      alt: 'Complete guide to Next.js Server Actions with code examples',
    },
  ],
}

// ❌ BAD: No alt text or generic alt
openGraph: {
  images: ['https://example.com/image.jpg'],  // No alt
  // OR
  images: [{ url: '...', alt: 'image' }],  // Too generic
}

3. Make Titles Engaging for Social

TYPESCRIPT
// ✅ GOOD: Social-optimized titles
export const metadata: Metadata = {
  // Page title: Professional, keyword-rich
  title: 'Next.js Server Actions Tutorial - Complete Guide',
  
  // OG title: Engaging, benefit-focused
  openGraph: {
    title: 'Build Faster Forms with Next.js Server Actions',
  },
  
  // Twitter title: Can be more casual
  twitter: {
    title: 'Next.js Server Actions are 🔥 - Complete Tutorial',
  },
};

// ❌ BAD: Same boring title everywhere
title: 'Tutorial'
openGraph: { title: 'Tutorial' }
twitter: { title: 'Tutorial' }

4. Include Fallback Images

TYPESCRIPT
// ✅ GOOD: Multiple images with fallback
openGraph: {
  images: [
    {
      url: post.coverImage || 'https://example.com/default-og.jpg',
      width: 1200,
      height: 630,
    },
    // Fallback
    {
      url: 'https://example.com/site-default.jpg',
      width: 1200,
      height: 630,
    },
  ],
}

// If primary image fails, platforms use second

5. Test on All Major Platforms

TYPESCRIPT
// ✅ GOOD: Test workflow
// 1. Deploy your changes
// 2. Test on Facebook Sharing Debugger
// 3. Test on Twitter Card Validator
// 4. Test on LinkedIn Post Inspector
// 5. Share on actual platforms
// 6. Clear cache if needed (click "Scrape Again")

// Platforms cache aggressively!
// Always verify on actual platforms after changes

Key Takeaways

  • 1200x630 pixels - recommended OG image size
  • ImageResponse - generate dynamic OG images
  • opengraph-image.tsx - file-based image generation
  • summary_large_image - best Twitter card type
  • Platform-specific - OG and Twitter can differ
  • Absolute URLs - always use full URLs for images
  • Alt text - accessibility and fallbacks
  • Test everywhere - Facebook, Twitter, LinkedIn validators

What's Next?

You've mastered Open Graph and social media cards! Next, we'll explore Sitemap and Robots.txt—generating dynamic sitemaps, configuring robots.txt for search engine crawlers, and optimizing your site for discoverability. You'll complete the SEO fundamentals for production-ready Next.js apps!

We'll cover sitemap.xml generation, robots.txt configuration, XML sitemap best practices, and ensuring search engines can properly crawl and index your content.

🎨 Design Matters

Great OG images significantly increase click-through rates. Invest time in creating eye-catching designs with clear text, consistent branding, and engaging visuals. A/B test different designs to see what works best for your audience!

Test Your Understanding

Question 1 of 4

What is the recommended size for Open Graph images?

Master Open Graph images and social media cards in Next.js! Learn to create stunning social previews.

Previous
Dynamic Metadata Generation
Next
Sitemap and Robots.txt

Complete Next.js SEO Mastery

Join 2,000+ developers building SEO-optimized Next.js apps. Get the final SEO lesson on sitemaps and robots.txt - 100% FREE.

You might also like

UI Components

Ready-made components for your projects

Developer Tools

Boost productivity with handy generators

HTML Game

Learn HTML concepts through interactive gameplay

Join Our Web Development Training Program

Master frontend & backend development with hands-on projects.

NextJS Tutorials

0 of 70 completed

Your Progress0%

Topics

Getting Started

  • What is Next.js?
  • Next.js vs React - Key Differences
  • Creating Your First Next.js 15 Project
  • Next.js Project Structure Explained
  • App Router vs Pages Router

Routing Fundamentals

  • File-Based Routing Basics
  • Creating Pages with page.tsx
  • Dynamic Routes and Route Parameters
  • Catch-All and Optional Catch-All Routes
  • Route Groups for Organization
  • Parallel Routes
  • Intercepting Routes

Layouts and Pages

  • Understanding Layouts
  • Root Layout and Global Configuration
  • Nested Layouts
  • Templates vs Layouts
  • Loading States with loading.tsx
  • Error Handling with error.tsx

Server and Client Components

  • Understanding Server Components
  • Client Components with 'use client'
  • When to Use Server vs Client Components
  • Component Composition Patterns
  • Passing Props Between Server and Client
  • Server Component Patterns and Best Practices

Data Fetching

  • Fetching Data in Server Components
  • Parallel and Sequential Data Fetching
  • Data Fetching Patterns and Strategies
  • Caching and Revalidation
  • Revalidate and Cache Tags
  • Handling Loading and Error States in Data Fetching

Navigation and Links

  • Link Component Basics
  • useRouter Hook for Programmatic Navigation
  • usePathname and useSearchParams Hooks
  • Active Links and Navigation States
  • Redirects and Navigation Guards

Styling in Next.js

  • CSS Modules in Next.js
  • Tailwind CSS Setup and Configuration
  • Global Styles and CSS Variables
  • Next.js Font Optimization

Images and Media

  • Next.js Image Component Basics
  • Image Optimization and Best Practices
  • Working with Static Assets

Forms and Data Mutations

  • Understanding Server Actions
  • Form Handling with Server Actions
  • Form Validation and Error Handling
  • useFormStatus and useFormState Hooks
  • Revalidating Data After Mutations
  • Optimistic Updates

Metadata and SEO

  • Static Metadata Configuration
  • Dynamic Metadata Generation
  • Open Graph and Social Media Cards
  • Sitemap and Robots.txt

API Routes and Route Handlers

  • Route Handlers Introduction
  • GET and POST Request Handlers
  • Dynamic API Routes
  • Request and Response Objects
  • API Error Handling and Status Codes

Middleware and Advanced Features

  • Introduction to Middleware
  • Authentication with Middleware
  • Environment Variables and Configuration
  • Streaming and Suspense
  • Not Found and Global Error Pages

Deployment and Optimization

  • Understanding Static and Dynamic Rendering
  • generateStaticParams for Static Generation
  • Build and Production Optimization
  • Deploying to Vercel
  • Alternative Deployment Options
  • Performance Monitoring and Analytics

Real-World Projects

  • Project 1: E-commerce Product Catalog
  • Project 2: Dashboard with Authentication
Falytom logoFALYTOM

Learn technologies like HTML, CSS, JavaScript, React, NodeJS and more through interactive games, tutorials, components and free tools. Transform your businesses with AI chatbots, SEO-optimized websites, and professional development services.

© 2025 Tomilola Group
  • Facebook logo
  • Twitter logo
  • Instagram logo
  • LinkedIn logo
  • GitHub logo
  • YouTube logo