Fix/seo metadata#563
Conversation
📝 WalkthroughWalkthroughCentralizes page metadata via a new metadata utility and updates layout/pages to use it, injects Organization JSON‑LD and theme color in RootLayout head, replaces imperative navigation with declarative links in call-to-action, adds Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Pull request overview
This PR improves SEO/social sharing metadata across the Next.js app by centralizing page metadata generation, adding structured data, and updating navigation/linking to use canonical paths.
Changes:
- Added a shared metadata utility and wired it into the root layout and select pages.
- Added JSON-LD structured data in the root layout
<head>. - Updated the home CTA to use standard links (external
<a>+ internal<Link>) and expanded the sitemap with the archive route.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/components/shared/utils/metadata.ts | Introduces centralized metadata generation and per-page canonical/title config. |
| src/components/pages/home/calltoaction.tsx | Switches CTA interactions from router/button to proper links for navigation and SEO. |
| src/app/layout.tsx | Uses shared metadata generator for home and injects Organization JSON-LD + theme color. |
| src/app/Communities/page.tsx | Adds per-page metadata via shared generator. |
| src/app/archive/page.tsx | Adds per-page metadata via shared generator. |
| public/sitemap.xml | Adds /archive entry to the sitemap. |
Comments suppressed due to low confidence (1)
src/app/archive/page.tsx:11
- This page component is named
Communities, but it renders the Archive page (<Archive />) and lives undersrc/app/archive. Renaming the function toArchive(or similar) will reduce confusion in stack traces and diagnostics.
export default function Communities() {
return (
<>
<Archive />
</>
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return { | ||
| title: metaConfig.title, | ||
| description: | ||
| 'Never miss an event from your favourite Tech Commnuity in Tamil Nadu. Discover upcoming developer conferences, tech meetups, and community events across Tamil Nadu. Stay updated with the latest technology events, workshops, and hackathons in Chennai, Coimbatore, Madurai and more.', |
There was a problem hiding this comment.
The meta description string contains a typo (“Tech Commnuity”). Since this content is used in SEO metadata, fix the spelling to avoid publishing it in <meta name="description">.
| 'Never miss an event from your favourite Tech Commnuity in Tamil Nadu. Discover upcoming developer conferences, tech meetups, and community events across Tamil Nadu. Stay updated with the latest technology events, workshops, and hackathons in Chennai, Coimbatore, Madurai and more.', | |
| 'Never miss an event from your favourite Tech Community in Tamil Nadu. Discover upcoming developer conferences, tech meetups, and community events across Tamil Nadu. Stay updated with the latest technology events, workshops, and hackathons in Chennai, Coimbatore, Madurai and more.', |
| export function generateMetadata(page: Page): Metadata { | ||
| const metaConfig = Metaconfig[page]; | ||
|
|
There was a problem hiding this comment.
Metaconfig is currently untyped, so Metaconfig[page] can become undefined at runtime without TypeScript catching it (leading to title/canonical being undefined). Consider typing it as a Record<Page, { canonical: string; title: string }> (or similar) and/or adding a defensive error when a page key is missing.
|
|
||
| export const Metaconfig = { | ||
| [Page.HOME]: { | ||
| canonical: '', |
There was a problem hiding this comment.
Home canonical is set to an empty string. Depending on how Next resolves it, this may render an empty/invalid canonical URL. Prefer an explicit path like '/' (or omit alternates.canonical for home) to avoid accidental <link rel="canonical" href=""> output.
| canonical: '', | |
| canonical: '/', |
| openGraph: { | ||
| title: 'Tamil Nadu Tech Events & Developer Conferences | TN Tech Community', | ||
| description: | ||
| 'Discover upcoming developer conferences, tech meetups, and community events across Tamil Nadu. Stay updated with the latest technology events, workshops, and hackathons.', | ||
| type: 'website', | ||
| locale: 'en_US', | ||
| siteName: 'Tamil Nadu Tech Events' | ||
| }, |
There was a problem hiding this comment.
The PR description mentions adding an OG image at /og-image.png, but there’s no og-image.png under public/ and the Open Graph metadata here doesn’t specify any images. Either add the file + reference it in openGraph.images, or update the PR description to match what’s actually shipped.
| <button | ||
| <a | ||
| href='https://github.com/fossuchennai/communities/blob/main/CONTRIBUTING.md' | ||
| target='_blank' |
There was a problem hiding this comment.
External links opened with target="_blank" should include rel="noopener noreferrer" to prevent the new page from getting access to window.opener (reverse tabnabbing).
| target='_blank' | |
| target='_blank' | |
| rel='noopener noreferrer' |
| const jsonLD = { | ||
| '@context': 'https://schema.org', | ||
| '@type': 'Organization', | ||
| name: 'TamilNadu Tech', | ||
| url: 'https://tamilnadu.tech/', | ||
| logo: 'https://tamilnadu.tech/favicon.ico', | ||
| sameAs: ['https://github.com/fossuchennai/communities'], |
There was a problem hiding this comment.
JSON-LD data hardcodes the site URL and logo URL even though the codebase already has a SITE_URL constant used for metadata elsewhere. Consider building these values from SITE_URL to avoid duplication and accidental drift if the canonical site URL changes.
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/app/layout.tsx (1)
63-63:⚠️ Potential issue | 🟠 MajorMove analytics provider inside
<body>.Line 63 renders
UmamiProvideras a direct child of<html>outside<body>, which can cause invalid document structure and hydration issues.🔧 Suggested fix
<body className={`${geistSans.variable} ${geistMono.variable} mx-auto max-w-[1120px] bg-[`#fafafa`] antialiased`} > <Header /> {children} <Footer /> + {IS_PROD && webId ? <UmamiProvider websiteId={webId} /> : null} </body> - {IS_PROD && <UmamiProvider websiteId={webId} />}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/layout.tsx` at line 63, The UmamiProvider is currently rendered as a direct child of <html> (rendered via IS_PROD && <UmamiProvider ... />), which breaks document structure; move the conditional rendering of UmamiProvider into the component returned inside the <body> element (e.g., alongside or inside the existing <body> children in your root Layout component) so that UmamiProvider is mounted as a descendant of <body> rather than at the top-level of the HTML.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@public/sitemap.xml`:
- Line 23: Update the <lastmod> value in the sitemap entry to the actual
modification date for this PR (replace the hardcoded 2025-05-06 with the correct
date, e.g., 2026-03-31) by editing the <lastmod> tag in public/sitemap.xml so it
reflects the real last-modified timestamp for the route.
In `@src/components/pages/home/calltoaction.tsx`:
- Around line 33-36: Update the external anchor element in CallToAction (the <a>
tag with href
'https://github.com/fossuchennai/communities/blob/main/CONTRIBUTING.md' and the
existing className) to include rel="noopener noreferrer" alongside
target="_blank" to prevent reverse-tabnabbing and preserve security when opening
the link in a new tab.
In `@src/components/shared/utils/metadata.ts`:
- Line 13: The metadata description contains a typo "Tech Commnuity"; update the
string value assigned to the description (locate the exported
metadata/description constant in src/components/shared/utils/metadata.ts) to
replace "Tech Commnuity" with "Tech Community" so the sentence reads correctly.
- Around line 16-29: The Open Graph and Twitter objects (openGraph and twitter)
are missing image metadata, so add the new OG image reference to both: add an
images array to openGraph (e.g., openGraph.images = [{ url: <OG_IMAGE_URL>, alt:
'Tamil Nadu Tech Events preview' }]) and add twitter.image or twitter.images
with the same URL and alt text so social previews use the intended image; update
the metadata.ts export that defines openGraph and twitter to include these
properties (use the existing OG image constant or URL you introduced).
---
Outside diff comments:
In `@src/app/layout.tsx`:
- Line 63: The UmamiProvider is currently rendered as a direct child of <html>
(rendered via IS_PROD && <UmamiProvider ... />), which breaks document
structure; move the conditional rendering of UmamiProvider into the component
returned inside the <body> element (e.g., alongside or inside the existing
<body> children in your root Layout component) so that UmamiProvider is mounted
as a descendant of <body> rather than at the top-level of the HTML.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 72afd463-2c7b-4c39-b223-f5ff6ea5d834
📒 Files selected for processing (6)
public/sitemap.xmlsrc/app/Communities/page.tsxsrc/app/archive/page.tsxsrc/app/layout.tsxsrc/components/pages/home/calltoaction.tsxsrc/components/shared/utils/metadata.ts
| </url> | ||
| <url> | ||
| <loc>https://tamilnadu.tech/archive</loc> | ||
| <lastmod>2025-05-06</lastmod> |
There was a problem hiding this comment.
Update lastmod to the actual modification date.
Line 23 still uses 2025-05-06. Since this route is being introduced/updated in this PR, use the real update date (e.g., 2026-03-31 if accurate).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@public/sitemap.xml` at line 23, Update the <lastmod> value in the sitemap
entry to the actual modification date for this PR (replace the hardcoded
2025-05-06 with the correct date, e.g., 2026-03-31) by editing the <lastmod> tag
in public/sitemap.xml so it reflects the real last-modified timestamp for the
route.
| openGraph: { | ||
| title: 'Tamil Nadu Tech Events & Developer Conferences | TN Tech Community', | ||
| description: | ||
| 'Discover upcoming developer conferences, tech meetups, and community events across Tamil Nadu. Stay updated with the latest technology events, workshops, and hackathons.', | ||
| type: 'website', | ||
| locale: 'en_US', | ||
| siteName: 'Tamil Nadu Tech Events' | ||
| }, | ||
| twitter: { | ||
| card: 'summary_large_image', | ||
| title: 'Tamil Nadu Tech Events & Developer Conferences', | ||
| description: | ||
| 'Discover upcoming developer conferences, tech meetups, and community events across Tamil Nadu.' | ||
| }, |
There was a problem hiding this comment.
Include OG/Twitter image metadata for social previews.
Open Graph and Twitter metadata here do not reference the new OG image, so shares may render without the intended preview image.
🔧 Suggested fix
openGraph: {
title: 'Tamil Nadu Tech Events & Developer Conferences | TN Tech Community',
description:
'Discover upcoming developer conferences, tech meetups, and community events across Tamil Nadu. Stay updated with the latest technology events, workshops, and hackathons.',
type: 'website',
locale: 'en_US',
- siteName: 'Tamil Nadu Tech Events'
+ siteName: 'Tamil Nadu Tech Events',
+ images: [
+ {
+ url: '/og-image.png',
+ width: 1200,
+ height: 630,
+ alt: 'Tamil Nadu Tech Events'
+ }
+ ]
},
twitter: {
card: 'summary_large_image',
title: 'Tamil Nadu Tech Events & Developer Conferences',
description:
- 'Discover upcoming developer conferences, tech meetups, and community events across Tamil Nadu.'
+ 'Discover upcoming developer conferences, tech meetups, and community events across Tamil Nadu.',
+ images: ['/og-image.png']
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| openGraph: { | |
| title: 'Tamil Nadu Tech Events & Developer Conferences | TN Tech Community', | |
| description: | |
| 'Discover upcoming developer conferences, tech meetups, and community events across Tamil Nadu. Stay updated with the latest technology events, workshops, and hackathons.', | |
| type: 'website', | |
| locale: 'en_US', | |
| siteName: 'Tamil Nadu Tech Events' | |
| }, | |
| twitter: { | |
| card: 'summary_large_image', | |
| title: 'Tamil Nadu Tech Events & Developer Conferences', | |
| description: | |
| 'Discover upcoming developer conferences, tech meetups, and community events across Tamil Nadu.' | |
| }, | |
| openGraph: { | |
| title: 'Tamil Nadu Tech Events & Developer Conferences | TN Tech Community', | |
| description: | |
| 'Discover upcoming developer conferences, tech meetups, and community events across Tamil Nadu. Stay updated with the latest technology events, workshops, and hackathons.', | |
| type: 'website', | |
| locale: 'en_US', | |
| siteName: 'Tamil Nadu Tech Events', | |
| images: [ | |
| { | |
| url: '/og-image.png', | |
| width: 1200, | |
| height: 630, | |
| alt: 'Tamil Nadu Tech Events' | |
| } | |
| ] | |
| }, | |
| twitter: { | |
| card: 'summary_large_image', | |
| title: 'Tamil Nadu Tech Events & Developer Conferences', | |
| description: | |
| 'Discover upcoming developer conferences, tech meetups, and community events across Tamil Nadu.', | |
| images: ['/og-image.png'] | |
| }, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/shared/utils/metadata.ts` around lines 16 - 29, The Open Graph
and Twitter objects (openGraph and twitter) are missing image metadata, so add
the new OG image reference to both: add an images array to openGraph (e.g.,
openGraph.images = [{ url: <OG_IMAGE_URL>, alt: 'Tamil Nadu Tech Events preview'
}]) and add twitter.image or twitter.images with the same URL and alt text so
social previews use the intended image; update the metadata.ts export that
defines openGraph and twitter to include these properties (use the existing OG
image constant or URL you introduced).
|
Why do we need all this? You can't share anything from the site. Can you please elaborate on this? |
Thanks for pointing that out! I was trying to improve how the site shows up in search and make it a bit easier for people to discover these communities. But yeah, I get your point — if this isn’t really needed here, I’m totally fine removing or simplifying it. Just let me know what you’d prefer 🙂 |
|
Preferably, do not add the share feature; you can work on SEO. Once done, please add the working Screenshot with Lighthouse Speed Insights and the SEO section. |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/components/shared/utils/metadata.ts (1)
11-12: Prefer page-specific descriptions instead of one global description.Right now all pages get the same description (Line 11), which reduces metadata relevance per route. Consider moving
descriptionintoMetaconfigper page.Also applies to: 22-32
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/shared/utils/metadata.ts` around lines 11 - 12, The current global description string should be removed and made page-specific by adding a description field to the Metaconfig object used per route; update the shared metadata export (the global description) to no longer supply a blanket description and instead ensure each page's Metaconfig instance includes its own description text, then update any functions that read the global description (e.g., code that imports the shared metadata or calls Metaconfig consumers) to prefer pageConfig.description from the page's Metaconfig and fall back to empty/undefined if absent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/pages/home/events.tsx`:
- Around line 210-216: The anchor element that renders the event link (the <a>
with aria-label `View event details for ${title}`) currently wraps the
AddToCalendar component, which itself renders a button, causing
interactive-in-interactive nesting; fix this by moving the AddToCalendar
component outside (siblings) of that anchor so the anchor contains only
non-interactive content (title, summary, image), and ensure AddToCalendar stays
keyboard-accessible as its own control; after moving, adjust surrounding
markup/CSS (the anchor's 'block' class or wrapper div) so visual layout is
preserved and update any aria attributes if needed to keep correct accessible
labels for both the link and the AddToCalendar button.
In `@src/components/shared/utils/metadata.ts`:
- Around line 27-30: The metadata title strings for Page.COMMUNITIES and
Page.ARCHIVE use "TamilNadu" without a space while other entries use "Tamil
Nadu"; update the title values for the Page.COMMUNITIES and Page.ARCHIVE entries
in the metadata object (in src/components/shared/utils/metadata.ts) to use the
consistent form "Tamil Nadu" so all Page.* titles match.
---
Nitpick comments:
In `@src/components/shared/utils/metadata.ts`:
- Around line 11-12: The current global description string should be removed and
made page-specific by adding a description field to the Metaconfig object used
per route; update the shared metadata export (the global description) to no
longer supply a blanket description and instead ensure each page's Metaconfig
instance includes its own description text, then update any functions that read
the global description (e.g., code that imports the shared metadata or calls
Metaconfig consumers) to prefer pageConfig.description from the page's
Metaconfig and fall back to empty/undefined if absent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f3b0dc70-54eb-4286-823f-868b10fefa3c
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (10)
public/robots.txtsrc/app/not-found.tsxsrc/app/page.tsxsrc/components/AddToCalendar.tsxsrc/components/github-button.tsxsrc/components/pages/home/calltoaction.tsxsrc/components/pages/home/events.tsxsrc/components/pages/home/hero.tsxsrc/components/shared/header.tsxsrc/components/shared/utils/metadata.ts
✅ Files skipped from review due to trivial changes (6)
- public/robots.txt
- src/components/shared/header.tsx
- src/app/not-found.tsx
- src/components/github-button.tsx
- src/app/page.tsx
- src/components/pages/home/hero.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/pages/home/calltoaction.tsx
| <a | ||
| href={link} | ||
| target='_blank' | ||
| rel='noopener noreferrer' | ||
| className='block' | ||
| aria-label={`View event details for ${title}`} | ||
| > |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify anchor blocks that contain AddToCalendar (interactive nesting risk)
rg -n --multiline --multiline-dotall '<a[^>]*>[\s\S]*?<AddToCalendar\b' --type=tsxRepository: FOSSUChennai/Communities
Length of output: 95
🏁 Script executed:
#!/bin/bash
# Find and examine the events.tsx file
find . -name "events.tsx" -type f 2>/dev/null
# Get the file size first
if [ -f "src/components/pages/home/events.tsx" ]; then
wc -l "src/components/pages/home/events.tsx"
fiRepository: FOSSUChennai/Communities
Length of output: 147
🏁 Script executed:
#!/bin/bash
# Read the specific lines from events.tsx (210-216 and surrounding context)
if [ -f "src/components/pages/home/events.tsx" ]; then
# Show lines 200-230 to get full context
sed -n '200,230p' "src/components/pages/home/events.tsx" | cat -n
fiRepository: FOSSUChennai/Communities
Length of output: 1383
🏁 Script executed:
#!/bin/bash
# Search for AddToCalendar component usage without type specification
rg -n 'AddToCalendar' src/components/pages/home/ -A 5 -B 5Repository: FOSSUChennai/Communities
Length of output: 1726
🏁 Script executed:
#!/bin/bash
# Read lines 210-250 to see the complete structure
sed -n '210,250p' "src/components/pages/home/events.tsx" | cat -nRepository: FOSSUChennai/Communities
Length of output: 2030
🏁 Script executed:
#!/bin/bash
# Read lines 240-260 to find the closing </a> tag
sed -n '240,260p' "src/components/pages/home/events.tsx" | cat -nRepository: FOSSUChennai/Communities
Length of output: 913
🏁 Script executed:
#!/bin/bash
# Verify that AddToCalendar is indeed a button component
find . -name "AddToCalendar*" -type f 2>/dev/null | head -5Repository: FOSSUChennai/Communities
Length of output: 102
🏁 Script executed:
#!/bin/bash
# Search for AddToCalendar component definition
rg -n 'export.*AddToCalendar|function AddToCalendar|const AddToCalendar' -A 10Repository: FOSSUChennai/Communities
Length of output: 922
🏁 Script executed:
#!/bin/bash
# Read the AddToCalendar component to see what it renders
cat -n "src/components/AddToCalendar.tsx"Repository: FOSSUChennai/Communities
Length of output: 2030
Remove nested button from anchor link.
The <a> tag (lines 210–253) wraps content that includes AddToCalendar, which renders an interactive <button> element. This creates interactive-in-interactive markup—a significant accessibility issue that breaks keyboard navigation, focus management, and semantic HTML. Screen readers and keyboard users will experience inconsistent behavior.
Move AddToCalendar outside the anchor:
Suggested restructure
<a
href={link}
target='_blank'
rel='noopener noreferrer'
className='block'
aria-label={`View event details for ${title}`}
>
...
- <AddToCalendar
- eventTitle={title}
- eventVenue={venue}
- eventDate={date}
- eventLink={link}
- />
...
</a>
+ <div className='mt-2'>
+ <AddToCalendar
+ eventTitle={title}
+ eventVenue={venue}
+ eventDate={date}
+ eventLink={link}
+ />
+ </div>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/pages/home/events.tsx` around lines 210 - 216, The anchor
element that renders the event link (the <a> with aria-label `View event details
for ${title}`) currently wraps the AddToCalendar component, which itself renders
a button, causing interactive-in-interactive nesting; fix this by moving the
AddToCalendar component outside (siblings) of that anchor so the anchor contains
only non-interactive content (title, summary, image), and ensure AddToCalendar
stays keyboard-accessible as its own control; after moving, adjust surrounding
markup/CSS (the anchor's 'block' class or wrapper div) so visual layout is
preserved and update any aria attributes if needed to keep correct accessible
labels for both the link and the AddToCalendar button.
| title: 'Communities | TamilNadu Tech Community' | ||
| }, | ||
| [Page.ARCHIVE]: { | ||
| title: 'Archive | TamilNadu Tech Community' |
There was a problem hiding this comment.
Fix title text consistency (“TamilNadu” vs “Tamil Nadu”).
Line 27 and Line 30 use TamilNadu (no space), while Line 24 uses Tamil Nadu. Keep one form consistently in metadata titles.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/shared/utils/metadata.ts` around lines 27 - 30, The metadata
title strings for Page.COMMUNITIES and Page.ARCHIVE use "TamilNadu" without a
space while other entries use "Tamil Nadu"; update the title values for the
Page.COMMUNITIES and Page.ARCHIVE entries in the metadata object (in
src/components/shared/utils/metadata.ts) to use the consistent form "Tamil Nadu"
so all Page.* titles match.

SEO Improvements
This PR enhances SEO and social sharing capabilities across the website.
Changes
generateMetadata/og-image.png) for rich link previewsTesting
Let me know if any improvements are needed. Thanks!
Summary by CodeRabbit
New Features
Improvements
Accessibility
Other