Vibe Code to codebykarun.com: SEO Enhancement with Structured Data (Part 9)
Part 4 of 5 in Evolution & Growth • Previously: Evolving with Series Support
After adding the series feature to Code by Karun, I realized we had a golden opportunity to enhance SEO. Series pages were the perfect candidate for rich structured data that would help search engines understand the content hierarchy and relationships.
This article documents how I went from basic meta tags to a comprehensive SEO strategy with JSON-LD structured data—all through conversational development.
The Problem: SEO for Series Pages
When I first launched the series feature, each series had its own dedicated page. But from an SEO perspective, they were just... pages. Search engines could see them, but they didn't understand:
- That this was a multi-part series
- How the articles related to each other
- The order and structure of content
- The breadcrumb navigation path
I wanted crawlers to have a complete "gist" of each series page. Not just keywords, but actual structured understanding.
The Initial Request
My starting point was straightforward:
"Let's make a special page for series tiles which should showcase series and show all the articles of series in same fashion as website shows other articles. Make this SEO friendly and add SEO for these pages based on series articles."
Then I clarified what I really wanted:
"SEO for this page should be dynamic based off of the series opened. Also add dynamic SEO which gives crawler a gist of the page whenever crawler opens it it would enhance the SEO"
Notice I didn't specify:
- What SEO techniques to use
- How to structure the data
- Which Schema.org types to implement
- How to generate descriptions
I just expressed the outcome: "Give crawlers a gist of the page."
Step 1: Enhanced Meta Tags
The AI started by upgrading the basic metadata to comprehensive, dynamic tags:
Before (Basic)
export const metadata: Metadata = {
title: `${series.title} | Code by Karun`,
description: series.description,
};
After (Comprehensive)
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const series = getSeriesById(params.id);
// Generate comprehensive description
const description = `${series.description} This ${series.totalArticles}-part series covers: ${series.articles.map((a) => a.title).slice(0, 3).join(', ')}${series.totalArticles > 3 ? ' and more' : ''}.`;
return {
title: `${series.title} | Code by Karun`,
description: description,
keywords: [
"series",
"tutorial series",
"guide",
"complete guide",
"step by step",
...allTags,
series.category,
...series.articles.flatMap((a) => a.tags),
],
authors: [{ name: "Karun" }],
creator: "Karun",
publisher: "Code by Karun",
openGraph: {
title: `${series.title} | Code by Karun`,
description: description,
type: "article",
publishedTime: series.firstPublished,
modifiedTime: series.lastPublished,
authors: ["Karun"],
tags: allTags,
siteName: "Code by Karun",
},
twitter: {
card: "summary_large_image",
title: `${series.title}`,
description: description,
creator: "@codebykarun",
},
alternates: {
canonical: `https://codebykarun.com/series/${params.id}`,
},
};
}
Key improvements:
- Dynamic description includes first 3 article titles
- Comprehensive keywords from ALL articles in series
- Author attribution properly structured
- OpenGraph for rich social media previews
- Twitter Cards for enhanced Twitter sharing
- Canonical URLs to prevent duplicate content issues
- Temporal metadata (published/modified dates)
This alone would help, but crawlers needed more structure.
Step 2: JSON-LD Structured Data
Here's where it gets powerful. The AI added JSON-LD (JavaScript Object Notation for Linked Data) scripts that tell search engines exactly what they're looking at.
Article Series Schema
const structuredData = {
"@context": "https://schema.org",
"@type": "Article",
"@id": `https://codebykarun.com/series/${params.id}`,
"headline": series.title,
"description": series.description,
"articleSection": series.category,
"datePublished": series.firstPublished,
"dateModified": series.lastPublished,
"author": {
"@type": "Person",
"name": "Karun",
"url": "https://codebykarun.com"
},
"publisher": {
"@type": "Organization",
"name": "Code by Karun",
"url": "https://codebykarun.com"
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": `https://codebykarun.com/series/${params.id}`
},
"keywords": allTags.join(", "),
"hasPart": series.articles.map((article, index) => ({
"@type": "Article",
"@id": `https://codebykarun.com/${series.category}/${article.slug}`,
"headline": article.title,
"description": article.excerpt,
"position": index + 1,
"datePublished": article.date,
"author": {
"@type": "Person",
"name": "Karun"
},
"url": `https://codebykarun.com/${series.category}/${article.slug}`,
"keywords": article.tags.join(", ")
})),
"isPartOf": {
"@type": "Series",
"name": series.title,
"numberOfItems": series.totalArticles
}
};
What this tells crawlers:
- This is an Article (specifically, a series)
- It has parts (hasPart property with ALL articles)
- Each part has a position (ordered 1, 2, 3...)
- Each part is also an Article (with its own metadata)
- They're related (isPartOf relationship)
- Complete URLs for every piece of content
- Temporal information (when published/modified)
- Author and publisher properly attributed
Breadcrumb Navigation Schema
const breadcrumbData = {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://codebykarun.com"
},
{
"@type": "ListItem",
"position": 2,
"name": "Series",
"item": "https://codebykarun.com/series"
},
{
"@type": "ListItem",
"position": 3,
"name": series.title,
"item": `https://codebykarun.com/series/${params.id}`
}
]
};
What this enables:
- Search results can show breadcrumb navigation
- Users see the path: Home > Series > {Series Name}
- Improves click-through rates
- Shows site structure to crawlers
Injecting the Structured Data
return (
<>
{/* JSON-LD Structured Data for SEO */}
<Script
id="series-structured-data"
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
/>
<Script
id="breadcrumb-structured-data"
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbData) }}
/>
<div className="...">
{/* Page content */}
</div>
</>
);
These <script>
tags are invisible to users but read by search engines. They provide a complete, machine-readable description of the page.
Step 3: Understanding Schema.org Types
The AI chose specific Schema.org types strategically:
Why "Article" for Series?
Google treats multi-part content as Articles when they're informational/educational. An Article can have:
hasPart
: Links to component articlesisPartOf
: Reverse relationship- Publication dates
- Author attribution
- Keywords and topics
Perfect for blog series.
Why "BreadcrumbList"?
Breadcrumb structured data:
- Shows site hierarchy
- Appears in search results (when Google chooses to show it)
- Helps users understand where they are
- Signals information architecture to crawlers
Alternative Approaches We Didn't Use
Collection/CollectionPage: Better for product listings or image galleries
Course: Requires specific educational properties (duration, prerequisites, etc.)
CreativeWorkSeries: Less specific than Article with hasPart
The Article + hasPart pattern was the best semantic fit.
What This Achieves
For Search Engines
-
Rich Snippets Eligibility
- Google can display enhanced results
- Article metadata shown in search
- Breadcrumbs may appear
- Published/modified dates visible
-
Content Understanding
- Crawlers know this is a series
- They understand article relationships
- They see the content structure
- They can index more intelligently
-
Knowledge Graph Signals
- Structured data feeds Google's Knowledge Graph
- Can improve entity recognition
- May enhance "People Also Ask" features
- Potentially appears in related content
For Users
-
Better Search Results
- More informative snippets
- Breadcrumb navigation visible
- Dates help assess freshness
- Clear content hierarchy
-
Social Media Sharing
- Rich previews on Twitter
- Complete OpenGraph data for Facebook
- LinkedIn shows proper metadata
- Better click-through rates
-
Discoverability
- Series appear as cohesive units
- Related articles easier to find
- Clear "start here" entry points
- Progress through series is logical
The Implementation Pattern
Here's the complete pattern for SEO-enhanced pages:
// 1. Generate comprehensive metadata
export async function generateMetadata({ params }) {
const data = getYourData(params.id);
return {
title: `${data.title} | Your Site`,
description: generateRichDescription(data),
keywords: extractAllRelevantKeywords(data),
authors: [{ name: "Your Name" }],
openGraph: { /* full OG tags */ },
twitter: { /* Twitter card */ },
alternates: { canonical: fullUrl },
};
}
// 2. Build structured data objects
function YourPage({ params }) {
const data = getYourData(params.id);
const structuredData = {
"@context": "https://schema.org",
"@type": "AppropriateType",
// ... all relevant properties
};
const breadcrumbData = {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
// ... navigation path
};
// 3. Inject as JSON-LD scripts
return (
<>
<Script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(structuredData)
}}
/>
<Script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(breadcrumbData)
}}
/>
{/* Your page content */}
</>
);
}
Real-World Example: Vibe Coding Journey Series
Let's see what crawlers see when they visit /series/vibe-coding-journey
:
Meta Tags
<title>Building codebykarun.com: The Vibe Coding Journey | Code by Karun</title>
<meta name="description" content="An 9-part series documenting how I built and evolved my blog using AI-assisted development and vibe coding techniques. This 9-part series covers: What is Vibe Coding?, Choosing the Right Tech Stack, The First Hour and more." />
<meta name="keywords" content="series, tutorial series, guide, complete guide, step by step, vibe coding, ai-assisted development, cursor ide, next.js, mdx, react, typescript, ..." />
Structured Data
{
"@type": "Article",
"headline": "Building codebykarun.com: The Vibe Coding Journey",
"hasPart": [
{
"@type": "Article",
"position": 1,
"headline": "What is Vibe Coding?",
"url": "https://codebykarun.com/coding/vibe-coding-journey-01-..."
},
// ... articles 2-9
],
"numberOfItems": 9
}
Crawlers see:
- A 9-part series about vibe coding
- Each article with title, URL, and position
- All relevant keywords and tags
- Publication timeline
- Author information
Testing Your Structured Data
Google provides free tools to validate:
Rich Results Test
- URL:
https://search.google.com/test/rich-results
- Paste your page URL
- See what Google extracts
- Check for errors/warnings
Schema Markup Validator
- URL:
https://validator.schema.org/
- Paste your JSON-LD
- Validates against Schema.org specs
- Shows warnings for improvements
Google Search Console
- Monitor actual performance
- See which rich results appear
- Track impressions and clicks
- Identify issues over time
What Made This Effective
1. Starting with Intent
I didn't ask for "JSON-LD structured data." I asked for "dynamic SEO that gives crawlers a gist of the page."
The AI chose JSON-LD because it's:
- The format Google recommends
- Easy to generate dynamically
- Doesn't interfere with HTML
- Supports complex relationships
2. Comprehensive, Not Minimal
The metadata isn't just title and description. It's:
- Complete OpenGraph for all platforms
- Twitter-specific cards
- Canonical URLs
- Author attribution
- Temporal data
- Keywords from all sources
"Make it SEO friendly" became "make it comprehensively optimized."
3. Semantic Accuracy
Using hasPart
to link articles isn't just technically correct—it semantically describes the relationship. This is how you communicate with crawlers effectively.
4. Dynamic Generation
Everything is computed from the data:
- Description includes article titles
- Keywords aggregate from all articles
- Dates come from actual publish dates
- URLs are generated consistently
No hardcoding. No manual updates. Just derive from the source.
Common SEO Mistakes to Avoid
❌ Static Metadata
// Bad: Same for every page
export const metadata = {
title: "Series Page",
description: "A series of articles"
};
✅ Dynamic Metadata
// Good: Unique per page
export async function generateMetadata({ params }) {
const series = getSeriesById(params.id);
return {
title: `${series.title} | Code by Karun`,
description: generateUniqueDescription(series),
// ...
};
}
❌ Incomplete Structured Data
{
"@type": "Article",
"headline": "My Series"
}
✅ Complete Structured Data
{
"@type": "Article",
"headline": "My Series",
"author": { "@type": "Person", "name": "..." },
"datePublished": "...",
"hasPart": [...],
// ... all relevant properties
}
❌ Wrong Schema Types
Using WebPage
for everything. Use specific types:
Article
for blog postsBreadcrumbList
for navigationPerson
for authorsOrganization
for publishers
The Vibe Coding Approach to SEO
Start with Outcomes
"Make it SEO friendly" → Comprehensive optimization strategy
Trust the Implementation
I didn't specify JSON-LD. The AI chose it because it's the right tool.
Validate the Results
Use Google's tools to verify what crawlers see.
Iterate Based on Data
Once live, use Search Console to see what's working and adjust.
Performance Considerations
Does JSON-LD Slow Down Pages?
No. It's:
- Small (few KB of JSON)
- Doesn't block rendering
- Not parsed by browser for display
- Only read by crawlers
Using Next.js Script Component
<Script
id="unique-id"
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
/>
Next.js optimizes script loading. This loads after the page hydrates.
Advanced SEO Patterns
For Individual Articles
Add structured data to article pages too:
const articleStructuredData = {
"@context": "https://schema.org",
"@type": "Article",
"headline": article.title,
"description": article.excerpt,
"author": { "@type": "Person", "name": "Karun" },
"datePublished": article.date,
// If part of series:
"isPartOf": {
"@type": "Series",
"name": series.title,
"url": `https://codebykarun.com/series/${series.id}`
}
};
For Category Pages
const categoryStructuredData = {
"@context": "https://schema.org",
"@type": "CollectionPage",
"name": "Coding Articles",
"description": "Technical articles about software development",
"hasPart": articles.map(article => ({
"@type": "Article",
"headline": article.title,
"url": articleUrl
}))
};
For Author/About Pages
const authorStructuredData = {
"@context": "https://schema.org",
"@type": "Person",
"name": "Karun",
"url": "https://codebykarun.com",
"jobTitle": "Software Developer",
"knowsAbout": ["JavaScript", "React", "Next.js", ...],
"sameAs": [
"https://twitter.com/codebykarun",
"https://github.com/yourhandle"
]
};
Monitoring SEO Impact
Short-term Indicators (Days to Weeks)
- Rich results in testing tools
- Correct metadata in "view source"
- No validation errors
- Proper rendering in social previews
Medium-term Indicators (Weeks to Months)
- Rich snippets appearing in search
- Improved click-through rates
- Better position for long-tail queries
- Breadcrumbs showing in results
Long-term Indicators (Months)
- Higher organic traffic
- More time on site
- Lower bounce rates
- Better engagement metrics
Key Takeaways
-
SEO is about communication
- You're telling crawlers what they're looking at
- Be clear, complete, and accurate
- Use structured data for precision
-
Dynamic generation is powerful
- Derive metadata from actual data
- No manual updates needed
- Always accurate and current
-
Comprehensive beats minimal
- Don't just add title/description
- Include OpenGraph, Twitter, canonical URLs
- Add JSON-LD structured data
- Think about every platform
-
Schema.org is your friend
- Learn the appropriate types
- Use them correctly
- Link entities together
- Be semantically accurate
-
Vibe coding works for SEO
- Describe the outcome you want
- Let AI choose the techniques
- Iterate based on testing
- Monitor real-world results
Practical Next Steps
If you want to enhance your site's SEO:
-
Audit current metadata
- Check each page type
- Look for missing or static meta tags
- Identify opportunities for structured data
-
Start with high-value pages
- Home page
- Main category pages
- Popular content
-
Add comprehensive metadata
- Dynamic title/description
- OpenGraph tags
- Twitter cards
- Canonical URLs
-
Implement structured data
- Choose appropriate Schema.org types
- Include all relevant properties
- Link related entities
- Add breadcrumbs
-
Test and validate
- Use Google's Rich Results Test
- Check Schema Markup Validator
- Verify in social media platforms
- Monitor Search Console
-
Iterate based on data
- Track performance
- Adjust based on results
- Expand to more page types
- Keep refining
The Complete SEO Stack for Code by Karun
Here's everything we implemented:
✅ Dynamic meta titles (page-specific)
✅ Rich descriptions (with content previews)
✅ Comprehensive keywords (aggregated from all sources)
✅ OpenGraph tags (full social media support)
✅ Twitter Cards (enhanced Twitter previews)
✅ Canonical URLs (prevent duplicate content)
✅ Author attribution (proper Schema.org Person)
✅ Publisher information (Schema.org Organization)
✅ Article structured data (JSON-LD with hasPart)
✅ Breadcrumb navigation (BreadcrumbList)
✅ Temporal metadata (published/modified dates)
✅ Series relationships (isPartOf, hasPart)
✅ Position indicators (ordered content)
✅ Keyword aggregation (from all articles)
Total implementation time: About 20 minutes of conversation.
Conclusion
SEO doesn't have to be mysterious or manual. With the right approach:
- Metadata can be dynamically generated
- Structured data can be comprehensive
- Crawlers can fully understand your content
- Users benefit from rich search results
All through conversational development.
The series pages on Code by Karun now give search engines a complete "gist" of what they contain. Every article, every relationship, every piece of metadata—all machine-readable and semantically accurate.
That's how you do SEO in 2024.
Next in the Series
👉 Continue to Part 5: Dual Analytics Strategy
Learn how to implement both Firebase and Vercel Analytics for comprehensive tracking—detailed custom events and performance metrics.
Want to see it in action? View the source of any series page on Code by Karun. Look for the <script type="application/ld+json">
tags. That's structured data telling crawlers exactly what they're looking at.