Vibe Code to codebykarun.com: SEO Enhancement with Structured Data (Part 9)

14 min read
vibe codingseostructured datajson-ldweb development

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:

  1. This is an Article (specifically, a series)
  2. It has parts (hasPart property with ALL articles)
  3. Each part has a position (ordered 1, 2, 3...)
  4. Each part is also an Article (with its own metadata)
  5. They're related (isPartOf relationship)
  6. Complete URLs for every piece of content
  7. Temporal information (when published/modified)
  8. Author and publisher properly attributed
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 articles
  • isPartOf: 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

  1. Rich Snippets Eligibility

    • Google can display enhanced results
    • Article metadata shown in search
    • Breadcrumbs may appear
    • Published/modified dates visible
  2. Content Understanding

    • Crawlers know this is a series
    • They understand article relationships
    • They see the content structure
    • They can index more intelligently
  3. 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

  1. Better Search Results

    • More informative snippets
    • Breadcrumb navigation visible
    • Dates help assess freshness
    • Clear content hierarchy
  2. Social Media Sharing

    • Rich previews on Twitter
    • Complete OpenGraph data for Facebook
    • LinkedIn shows proper metadata
    • Better click-through rates
  3. 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 posts
  • BreadcrumbList for navigation
  • Person for authors
  • Organization 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

  1. SEO is about communication

    • You're telling crawlers what they're looking at
    • Be clear, complete, and accurate
    • Use structured data for precision
  2. Dynamic generation is powerful

    • Derive metadata from actual data
    • No manual updates needed
    • Always accurate and current
  3. Comprehensive beats minimal

    • Don't just add title/description
    • Include OpenGraph, Twitter, canonical URLs
    • Add JSON-LD structured data
    • Think about every platform
  4. Schema.org is your friend

    • Learn the appropriate types
    • Use them correctly
    • Link entities together
    • Be semantically accurate
  5. 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:

  1. Audit current metadata

    • Check each page type
    • Look for missing or static meta tags
    • Identify opportunities for structured data
  2. Start with high-value pages

    • Home page
    • Main category pages
    • Popular content
  3. Add comprehensive metadata

    • Dynamic title/description
    • OpenGraph tags
    • Twitter cards
    • Canonical URLs
  4. Implement structured data

    • Choose appropriate Schema.org types
    • Include all relevant properties
    • Link related entities
    • Add breadcrumbs
  5. Test and validate

    • Use Google's Rich Results Test
    • Check Schema Markup Validator
    • Verify in social media platforms
    • Monitor Search Console
  6. 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.