-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
feat: add opengraph and twitter metadata for better social media sharing #448
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: add opengraph and twitter metadata for better social media sharing #448
Conversation
|
@mmd-rehan is attempting to deploy a commit to the Ixartz's projects Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughThe changes introduce comprehensive SEO, Open Graph, and Twitter metadata to the root layout, including detailed robots directives and social media image specifications. Additionally, English and French locale JSON files are updated to include new meta description fields for the Dashboard and UserProfile sections, enhancing metadata localization. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Browser
participant NextJS_App
participant Social_Media_Bot
User->>Browser: Requests page
Browser->>NextJS_App: Loads layout.tsx
NextJS_App-->>Browser: Sends HTML with enriched metadata
Social_Media_Bot->>NextJS_App: Crawls page for preview
NextJS_App-->>Social_Media_Bot: Returns metadata (OpenGraph, Twitter, robots, i18n descriptions)
Estimated code review effort🎯 2 (Simple) | ⏱️ ~7 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changesNo out-of-scope changes found. Suggested labels
Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. 📜 Recent review detailsConfiguration used: .coderabbit.yaml 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 15
♻️ Duplicate comments (1)
src/app/[locale]/(marketing)/portfolio/page.tsx (1)
17-55: Same metadata duplication pattern continues.This file exhibits the same code duplication issues identified in the previous files - hardcoded URLs, static images, and repeated locale mapping logic.
Refer to the comprehensive refactoring suggestion in the about page review for a systematic solution to eliminate this duplication across all marketing pages.
🧹 Nitpick comments (5)
src/app/[locale]/(marketing)/portfolio/[slug]/page.tsx (1)
40-47: Consider using dynamic OpenGraph images.The same static image is used across all portfolio items. The AI summary mentions a
getOgImagePathutility function that could provide more appropriate images for different content types.- images: [ - { - url: '/assets/images/nextjs-starter-banner.png', - width: 1200, - height: 630, - alt: title, - }, - ], + images: [ + { + url: getOgImagePath('portfolio', slug), + width: 1200, + height: 630, + alt: title, + }, + ],src/app/[locale]/(marketing)/page.tsx (1)
28-35: Standardize OpenGraph image selection across pages.Using the same static banner image for all pages may not provide the best social media preview experience.
Consider using dynamic images based on page type:
- url: '/assets/images/nextjs-starter-banner.png', + url: getOgImagePath('home'),src/utils/Metadata.test.ts (2)
4-23: Consider making the base URL configurable for different environments.The tests comprehensively cover URL generation for different locales and paths. However, the hardcoded domain
demo.nextjs-boilerplate.commight cause issues in different environments.Consider extracting the base URL to a configuration constant or environment variable:
+const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || 'https://demo.nextjs-boilerplate.com'; + describe('generateMetadataUrl', () => { it('should generate correct URL for English locale', () => { const url = generateMetadataUrl('/about', 'en'); - expect(url).toBe('https://demo.nextjs-boilerplate.com/en/about'); + expect(url).toBe(`${BASE_URL}/en/about`); });
46-123: Excellent metadata structure following OpenGraph and Twitter Card specifications.The mock metadata object comprehensively covers all required properties for social sharing, with proper image dimensions (1200x630) and thorough validation tests.
Consider adding type annotation to the mock metadata for better type safety:
+import type { Metadata } from 'next'; + -const mockMetadata = { +const mockMetadata: Metadata = {src/app/[locale]/layout.tsx (1)
10-60: Comprehensive metadata implementation following OpenGraph and Twitter Card best practices.The metadata structure includes all essential properties for SEO and social media sharing, with proper image dimensions (1200x630) and comprehensive OpenGraph/Twitter Card coverage.
Consider extracting social media handles and site information to constants for easier maintenance:
const SITE_CONFIG = { name: 'Next.js Boilerplate', description: 'Next.js Boilerplate is the perfect starter code for your project. Build your React application with the Next.js framework.', url: 'https://demo.nextjs-boilerplate.com', creator: 'CreativeDesignsGuru', twitter: '@ixartz', } as const;
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
src/app/[locale]/(auth)/(center)/sign-in/[[...sign-in]]/page.tsx(1 hunks)src/app/[locale]/(auth)/(center)/sign-up/[[...sign-up]]/page.tsx(1 hunks)src/app/[locale]/(auth)/dashboard/page.tsx(1 hunks)src/app/[locale]/(auth)/dashboard/user-profile/[[...user-profile]]/page.tsx(1 hunks)src/app/[locale]/(marketing)/about/page.tsx(1 hunks)src/app/[locale]/(marketing)/counter/page.tsx(1 hunks)src/app/[locale]/(marketing)/page.tsx(1 hunks)src/app/[locale]/(marketing)/portfolio/[slug]/page.tsx(1 hunks)src/app/[locale]/(marketing)/portfolio/page.tsx(1 hunks)src/app/[locale]/layout.tsx(1 hunks)src/utils/Helpers.ts(1 hunks)src/utils/Metadata.test.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
⚙️ CodeRabbit Configuration File
Review the Typescript and React code for conformity with best practices in React, and Typescript. Highlight any deviations.
Files:
src/utils/Metadata.test.tssrc/app/[locale]/(auth)/(center)/sign-up/[[...sign-up]]/page.tsxsrc/app/[locale]/(auth)/dashboard/user-profile/[[...user-profile]]/page.tsxsrc/app/[locale]/(auth)/dashboard/page.tsxsrc/utils/Helpers.tssrc/app/[locale]/(marketing)/about/page.tsxsrc/app/[locale]/(marketing)/counter/page.tsxsrc/app/[locale]/(marketing)/page.tsxsrc/app/[locale]/(marketing)/portfolio/[slug]/page.tsxsrc/app/[locale]/(auth)/(center)/sign-in/[[...sign-in]]/page.tsxsrc/app/[locale]/(marketing)/portfolio/page.tsxsrc/app/[locale]/layout.tsx
🔇 Additional comments (4)
src/utils/Helpers.ts (1)
39-47: LGTM! Clean implementation with good TypeScript practices.The function properly uses union types, provides a sensible default parameter, and implements a clear mapping strategy for different page types.
src/utils/Metadata.test.ts (2)
1-3: LGTM! Clean test setup with proper imports.The Vitest imports and utility function imports are correctly structured for comprehensive testing.
25-44: LGTM! Comprehensive coverage of OG image path generation.The tests properly validate the different image paths for various contexts (default, auth, dashboard), ensuring the correct image is selected based on the page type.
src/app/[locale]/layout.tsx (1)
50-60: LGTM! Proper robots configuration for optimal SEO.The robots metadata is correctly configured to allow indexing and following, with Google-specific settings that enable rich snippets and large image previews for better search visibility.
src/app/[locale]/(auth)/(center)/sign-in/[[...sign-in]]/page.tsx
Outdated
Show resolved
Hide resolved
src/app/[locale]/(auth)/(center)/sign-in/[[...sign-in]]/page.tsx
Outdated
Show resolved
Hide resolved
src/app/[locale]/(auth)/(center)/sign-up/[[...sign-up]]/page.tsx
Outdated
Show resolved
Hide resolved
| images: [ | ||
| { | ||
| url: '/assets/images/nextjs-boilerplate-sign-up.png', | ||
| width: 1200, | ||
| height: 630, | ||
| alt: title, | ||
| }, | ||
| ], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Image path inconsistency with utility function.
The sign-up page uses /assets/images/nextjs-boilerplate-sign-up.png, but this path doesn't exist in the getOgImagePath utility function. Either update the utility function to include a 'signup' type or use the existing 'auth' type.
Let me check what image types are available in the utility function:
Also applies to: 44-44
🏁 Script executed:
#!/bin/bash
# Check the getOgImagePath function and available image assets
rg -A 10 "getOgImagePath" src/utils/Helpers.ts
find . -name "*.png" -path "*/assets/images/*" | head -10Length of output: 903
Add a signup type in getOgImagePath and use it in the sign-up page
The nextjs-boilerplate-sign-up.png asset is already in public/assets/images, but getOgImagePath only handles 'default' | 'auth' | 'dashboard'. To keep OG image paths consistent:
• In src/utils/Helpers.ts
- Extend the union to include
signup - Add a
signupentry toimageMap
export function getOgImagePath(
pageType: 'default' | 'auth' | 'dashboard' | 'signup' = 'default',
): string {
const imageMap = {
default: '/assets/images/nextjs-starter-banner.png',
auth: '/assets/images/nextjs-boilerplate-sign-in.png',
signup: '/assets/images/nextjs-boilerplate-sign-up.png',
dashboard: '/assets/images/nextjs-boilerplate-saas.png',
};
return imageMap[pageType];
}• In src/app/[locale]/(auth)/(center)/sign-up/[[...sign-up]]/page.tsx
- Import the helper:
import { getOgImagePath } from 'src/utils/Helpers';
- Replace the hard-coded URL with the utility call:
images: [ { - url: '/assets/images/nextjs-boilerplate-sign-up.png', + url: getOgImagePath('signup'), width: 1200, height: 630, alt: title, }, ],
This ensures all pages use the same OG-image utility rather than hard-coding assets.
🤖 Prompt for AI Agents
In src/app/[locale]/(auth)/(center)/sign-up/[[...sign-up]]/page.tsx around lines
29 to 36, replace the hard-coded OG image URL with a call to getOgImagePath by
first importing getOgImagePath from src/utils/Helpers. Then, in
src/utils/Helpers.ts, extend the getOgImagePath function's pageType union to
include 'signup' and add a corresponding 'signup' entry in the imageMap pointing
to '/assets/images/nextjs-boilerplate-sign-up.png'. This will centralize OG
image path management and keep the sign-up page consistent with other pages.
| export const metadata: Metadata = { | ||
| metadataBase: new URL('https://demo.nextjs-boilerplate.com'), | ||
| title: { | ||
| default: 'Next.js Boilerplate', | ||
| template: '%s | Next.js Boilerplate', | ||
| }, | ||
| description: 'Next.js Boilerplate is the perfect starter code for your project. Build your React application with the Next.js framework.', | ||
| keywords: ['Next.js', 'React', 'TypeScript', 'Tailwind CSS', 'Boilerplate', 'Starter'], | ||
| authors: [{ name: 'CreativeDesignsGuru' }], | ||
| creator: 'CreativeDesignsGuru', | ||
| publisher: 'CreativeDesignsGuru', | ||
| formatDetection: { | ||
| email: false, | ||
| address: false, | ||
| telephone: false, | ||
| }, | ||
| openGraph: { | ||
| type: 'website', | ||
| locale: 'en_US', | ||
| url: 'https://demo.nextjs-boilerplate.com', | ||
| siteName: 'Next.js Boilerplate', | ||
| title: 'Next.js Boilerplate', | ||
| description: 'Next.js Boilerplate is the perfect starter code for your project. Build your React application with the Next.js framework.', | ||
| images: [ | ||
| { | ||
| url: '/assets/images/nextjs-starter-banner.png', | ||
| width: 1200, | ||
| height: 630, | ||
| alt: 'Next.js Boilerplate', | ||
| }, | ||
| ], | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
❓ Verification inconclusive
Consider dynamic metadata generation for proper i18n support.
The static metadata doesn't adapt to different locales, which may impact the i18n objectives mentioned in the PR. The hardcoded locale: 'en_US' in OpenGraph won't be appropriate for French pages.
Consider implementing generateMetadata function instead of static metadata to support locale-specific content:
export async function generateMetadata(props: {
params: Promise<{ locale: string }>;
}): Promise<Metadata> {
const { locale } = await props.params;
return {
// ... existing metadata
openGraph: {
// ... existing openGraph
locale: locale === 'fr' ? 'fr_FR' : 'en_US',
url: `https://demo.nextjs-boilerplate.com/${locale}`,
},
};
}Let me verify if there are locale-specific metadata implementations in other files:
🏁 Script executed:
#!/bin/bash
# Search for generateMetadata functions that handle locale-specific metadata
ast-grep --pattern 'export async function generateMetadata($$$) {
$$$
}'Length of output: 76
Implement dynamic metadata generation for i18n support
No existing generateMetadata function was found in the codebase. To ensure your metadata (including OpenGraph locale and URLs) adapts to each locale, replace the static export const metadata in src/app/[locale]/layout.tsx with an async generateMetadata function:
• Remove the block at lines 10–41 that reads:
export const metadata: Metadata = { … }• Add something like:
export async function generateMetadata({
params: { locale },
}: {
params: { locale: string }
}): Promise<Metadata> {
const isFR = locale === 'fr';
return {
metadataBase: new URL(`https://demo.nextjs-boilerplate.com/${locale}`),
title: {
default: isFR ? 'Next.js Boilerplate FR' : 'Next.js Boilerplate',
template: '%s | Next.js Boilerplate',
},
description: isFR
? 'Next.js Boilerplate est le code de démarrage parfait pour votre projet. Build your React application avec Next.js.'
: 'Next.js Boilerplate is the perfect starter code for your project. Build your React application with the Next.js framework.',
keywords: ['Next.js', 'React', 'TypeScript', 'Tailwind CSS', 'Boilerplate', 'Starter'],
authors: [{ name: 'CreativeDesignsGuru' }],
creator: 'CreativeDesignsGuru',
publisher: 'CreativeDesignsGuru',
formatDetection: { email: false, address: false, telephone: false },
openGraph: {
type: 'website',
locale: isFR ? 'fr_FR' : 'en_US',
url: `https://demo.nextjs-boilerplate.com/${locale}`,
siteName: 'Next.js Boilerplate',
title: isFR ? 'Next.js Boilerplate FR' : 'Next.js Boilerplate',
description: isFR
? 'Next.js Boilerplate est le code de démarrage parfait pour votre projet. Build your React application avec Next.js.'
: 'Next.js Boilerplate is the perfect starter code for your project. Build your React application with the Next.js framework.',
images: [
{
url: '/assets/images/nextjs-starter-banner.png',
width: 1200,
height: 630,
alt: 'Next.js Boilerplate',
},
],
},
};
}This ensures your metadata—and especially the OpenGraph locale—correctly reflects the user’s locale.
🤖 Prompt for AI Agents
In src/app/[locale]/layout.tsx from lines 10 to 41, replace the static export
const metadata object with an async generateMetadata function that accepts the
locale parameter. Remove the existing metadata export and implement
generateMetadata to return metadata dynamically based on the locale, adjusting
metadataBase URL, title, description, and openGraph locale and URL accordingly
to support i18n. This will ensure metadata and OpenGraph data reflect the user's
locale properly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/utils/Helpers.ts (1)
36-39: Use existinggetBaseUrl()function instead of hardcoding the URL.The function hardcodes
'https://demo.nextjs-boilerplate.com'when there's already agetBaseUrl()function that handles environment variables and different deployment scenarios properly.
🧹 Nitpick comments (2)
src/utils/Helpers.ts (2)
41-50: Consider optimizing for performance and type safety.The function works correctly but could be improved for better performance and type safety.
Move the imageMap outside the function to avoid recreation on each call and add
as constfor better type safety:+const IMAGE_MAP = { + default: '/assets/images/nextjs-starter-banner.png', + signup: '/assets/images/nextjs-boilerplate-sign-up.png', + auth: '/assets/images/nextjs-boilerplate-sign-in.png', + dashboard: '/assets/images/nextjs-boilerplate-saas.png', +} as const; + export function getOgImagePath(pageType: PageType = 'default'): string { - const imageMap = { - default: '/assets/images/nextjs-starter-banner.png', - signup: '/assets/images/nextjs-boilerplate-sign-up.png', - auth: '/assets/images/nextjs-boilerplate-sign-in.png', - dashboard: '/assets/images/nextjs-boilerplate-saas.png', - }; - - return imageMap[pageType]; + return IMAGE_MAP[pageType]; }
52-61: Consider using object mapping for better maintainability.The switch statement works but an object mapping would be more maintainable and easier to extend as more locales are added.
+const LOCALE_MAP: Record<string, string> = { + en: 'en_US', + fr: 'fr_FR', +}; + export const getOpenGraphLocale = (locale: string): string => { - switch (locale) { - case 'en': - return 'en_US'; - case 'fr': - return 'fr_FR'; - default: - return 'en_US'; - } + return LOCALE_MAP[locale] || 'en_US'; };
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
src/app/[locale]/(auth)/(center)/sign-in/[[...sign-in]]/page.tsx(2 hunks)src/app/[locale]/(auth)/(center)/sign-up/[[...sign-up]]/page.tsx(2 hunks)src/app/[locale]/(auth)/dashboard/page.tsx(2 hunks)src/app/[locale]/(auth)/dashboard/user-profile/[[...user-profile]]/page.tsx(2 hunks)src/app/[locale]/(marketing)/about/page.tsx(2 hunks)src/app/[locale]/(marketing)/counter/page.tsx(2 hunks)src/app/[locale]/(marketing)/page.tsx(2 hunks)src/app/[locale]/(marketing)/portfolio/[slug]/page.tsx(2 hunks)src/app/[locale]/(marketing)/portfolio/page.tsx(2 hunks)src/app/[locale]/layout.tsx(1 hunks)src/locales/en.json(1 hunks)src/locales/fr.json(1 hunks)src/utils/Helpers.ts(2 hunks)
✅ Files skipped from review due to trivial changes (2)
- src/locales/en.json
- src/locales/fr.json
🚧 Files skipped from review as they are similar to previous changes (10)
- src/app/[locale]/(marketing)/portfolio/[slug]/page.tsx
- src/app/[locale]/(auth)/dashboard/page.tsx
- src/app/[locale]/(marketing)/portfolio/page.tsx
- src/app/[locale]/(auth)/(center)/sign-in/[[...sign-in]]/page.tsx
- src/app/[locale]/(marketing)/page.tsx
- src/app/[locale]/(marketing)/about/page.tsx
- src/app/[locale]/layout.tsx
- src/app/[locale]/(auth)/dashboard/user-profile/[[...user-profile]]/page.tsx
- src/app/[locale]/(marketing)/counter/page.tsx
- src/app/[locale]/(auth)/(center)/sign-up/[[...sign-up]]/page.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
⚙️ CodeRabbit Configuration File
Review the Typescript and React code for conformity with best practices in React, and Typescript. Highlight any deviations.
Files:
src/utils/Helpers.ts
🔇 Additional comments (1)
src/utils/Helpers.ts (1)
3-3: LGTM!The
PageTypedefinition follows TypeScript best practices with clear string literal types that provide type safety and good IntelliSense support.
|
Thank you for opening the PR, I just feel there are too much duplicate, is it possible to make the feature lighter? it seems to be very heavy and the boilerplate will become harder to understand. Is it possible to use the same og image for all pages, it will make the feature lighter. For example, add this feature only in layout.ts so all pages will inherits the same image. |
|
@ixartz I agree with you, it might be overwhelming for someone at first look, and for some clones it might not be necessary. I was trying to create something common helper to generate metaData something like: is when i try to make it common, i am not able to get translations i mean generally we wouldn’t create types for translation files (.json) what could be the appropriate solution? |
Fixes #447
Summary by CodeRabbit
New Features
Documentation