Skip to content

ENG-1655 - Refactor blog structure and add new features#954

Merged
mdroidian merged 3 commits intomainfrom
eng-1631-migrate-blog-index-and-blog-post-templates-to-nextra
Apr 12, 2026
Merged

ENG-1655 - Refactor blog structure and add new features#954
mdroidian merged 3 commits intomainfrom
eng-1631-migrate-blog-index-and-blog-post-templates-to-nextra

Conversation

@mdroidian
Copy link
Copy Markdown
Member

@mdroidian mdroidian commented Apr 12, 2026


Open with Devin

Summary by CodeRabbit

Release Notes

  • New Features

    • Blog system infrastructure modernized with enhanced support for multiple content formats (Markdown and MDX)
    • Improved blog post discovery and metadata organization
  • Documentation

    • Added comprehensive blog post creation guide and template demonstrating frontmatter configuration and content authoring patterns

- Updated blog directory path to use process.cwd() for better content management.
- Introduced blog schema validation using Zod for frontmatter.
- Added a new layout component for blog pages to enhance styling.
- Refactored blog index and post pages to utilize dynamic imports and improve metadata handling.
- Implemented functions to retrieve blog data and handle slugs more effectively.
- Migrated example blog post to new content directory structure and created a new index file for blog updates.
@linear-code
Copy link
Copy Markdown

linear-code Bot commented Apr 12, 2026

@supabase
Copy link
Copy Markdown

supabase Bot commented Apr 12, 2026

This pull request has been ignored for the connected project zytfjzqyijgagqxrzbmz because there are no changes detected in packages/database/supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@mdroidian mdroidian changed the title ENG-1655: Refactor blog structure and add new features ENG-1655 - Refactor blog structure and add new features Apr 12, 2026
@mdroidian
Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 12, 2026

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 12, 2026

📝 Walkthrough

Walkthrough

The pull request migrates the blog system from filesystem-based markdown loading to Nextra-driven dynamic imports. Changes include adding Zod schema validation for blog frontmatter, restructuring blog data access with a new getBlogBySlug function, updating slug generation to support both .md and .mdx files, adding blog layout styling, and relocating blog content to a new directory structure.

Changes

Cohort / File(s) Summary
Blog Schema & Configuration
apps/website/app/(home)/blog/blogSchema.ts, apps/website/app/(home)/blog/blogDirectory.ts
Added Zod schemas for blog frontmatter validation with fields for title, date, author, published status, tags, and description. Changed blog directory path from module-URL-based computation to fixed process.cwd()/content/blog.
Blog Reading & Processing
apps/website/app/(home)/blog/readBlogs.tsx
Refactored to support both .md and .mdx files, added schema-based validation replacing previous PageSchema, implemented new getBlogBySlug() function for slug-based blog lookup, updated getAllBlogs() to filter by published status and sort by date, and added index file exclusion logic.
Blog Page Components
apps/website/app/(home)/blog/[slug]/page.tsx, apps/website/app/(home)/blog/page.tsx, apps/website/app/(home)/blog/layout.tsx
Migrated to Nextra's importPage for dynamic content loading, replaced filesystem operations with structured metadata merging, added conditional H1 rendering based on source content analysis, and introduced layout component with Nextra CSS imports.
Blog Content Files
apps/website/app/(home)/blog/posts/EXAMPLE.md, apps/website/content/blog/EXAMPLE.mdx, apps/website/content/blog/index.mdx
Removed legacy example post from posts/ directory and added new MDX-based blog templates in content/blog/ directory with updated frontmatter structure and Nextra component examples.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client Request
    participant PageComponent as Blog Page Component
    participant Nextra as Nextra importPage()
    participant BlogLoader as readBlogs (getBlogBySlug)
    participant Schema as Zod Schema
    participant Renderer as MDX Renderer

    Client->>PageComponent: Request /blog/[slug]
    
    PageComponent->>Nextra: importPage(slug)
    Nextra-->>PageComponent: Returns MDXContent, metadata, sourceCode
    
    PageComponent->>BlogLoader: getBlogBySlug(slug)
    BlogLoader->>Schema: validate(frontmatter)
    Schema-->>BlogLoader: BlogData or null
    BlogLoader-->>PageComponent: Blog data (title, date, author, etc.)
    
    PageComponent->>PageComponent: Merge Nextra metadata + Blog data
    PageComponent->>PageComponent: Check if sourceCode contains H1
    
    PageComponent->>Renderer: Render MDXContent
    Renderer-->>PageComponent: Rendered HTML
    
    PageComponent->>Client: Page with conditionally rendered title + content
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'ENG-1655 - Refactor blog structure and add new features' is partially related to the changeset. It references refactoring and new features, but lacks specificity about the core change: migrating from filesystem/markdown-based blog loading to Nextra-driven dynamic importing. Consider a more specific title that highlights the primary technical change, such as 'Migrate blog system to Nextra-based dynamic importing' or 'ENG-1655 - Migrate blog to Nextra with dynamic imports and improved metadata handling'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot]

This comment was marked as resolved.

@mdroidian mdroidian merged commit fe6a8cd into main Apr 12, 2026
7 checks passed
@mdroidian mdroidian deleted the eng-1631-migrate-blog-index-and-blog-post-templates-to-nextra branch April 12, 2026 04:08
export const BlogFrontmatterSchema = z.object({
title: z.string(),
published: z.boolean().default(false),
date: z.string(),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The date field is validated as a generic string but is later used for date parsing and sorting. Invalid date strings (e.g., "not-a-date") will pass validation but cause NaN values when parsed with new Date() in the sort function (readBlogs.tsx:43-44), leading to unpredictable sort order.

date: z.string().refine(
  (val) => !isNaN(Date.parse(val)),
  { message: "Must be a valid date string" }
),
Suggested change
date: z.string(),
date: z.string().refine((val) => !isNaN(Date.parse(val)), { message: "Must be a valid date string" }),

Spotted by Graphite

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 new potential issues.

View 8 additional findings in Devin Review.

Open in Devin Review

Comment on lines +15 to +16
const hasPrimaryHeading = (sourceCode: string): boolean =>
/(^|\n)#\s+\S/m.test(sourceCode);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 hasPrimaryHeading regex matches # inside fenced code blocks, suppressing the blog post title

The hasPrimaryHeading function uses the regex /(^|\n)#\s+\S/m to detect whether MDX source code already contains an H1 heading. However, this regex operates on raw source text and will match # at the start of any line—including inside fenced code blocks (e.g., bash/Python comments like # install deps). When a blog post has no actual H1 heading but contains a code block with a # comment line, the function returns true, and the prominent <h1> with the blog title is not rendered (apps/website/app/(home)/blog/[slug]/page.tsx:86-89). The post then appears without any visible title. The same function is duplicated in the blog index page at apps/website/app/(home)/blog/page.tsx:8-9.

Prompt for agents
The hasPrimaryHeading function in both apps/website/app/(home)/blog/[slug]/page.tsx (line 15-16) and apps/website/app/(home)/blog/page.tsx (line 8-9) uses a naive regex that matches # at the start of any line, including inside fenced code blocks. This causes false positives when a blog post contains e.g. a bash code block with a # comment but no actual H1 heading, resulting in the blog title not being rendered.

A more robust approach would be to skip content inside fenced code blocks (``` or ~~~) before testing for headings. For example, strip fenced code blocks first with a regex like /^(`{3,}|~{3,}).*\n[\s\S]*?^\1/gm, then test the remaining text. Alternatively, since Nextra compiles MDX to an AST, you could inspect the compiled output or the table of contents for an H1 rather than regex-matching raw source.

Also, this function is duplicated in two files. Per AGENTS.md guidelines (prefer util functions for reusable logic), extract it into a shared utility in the blog directory.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

title: z.string(),
published: z.boolean().default(false),
date: z.string(),
author: z.string(),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 YAML null for tags (or description) silently drops the entire blog post

In YAML, a key with no value (e.g., tags: on its own line) parses as null via gray-matter/js-yaml. Zod's .optional() only accepts undefined, not null, so null falls through to BlogTagsSchema which is z.union([z.array(z.string()), z.string()]) — neither branch accepts null, causing a ZodError. This error is caught by processBlogFile at apps/website/app/(home)/blog/readBlogs.tsx:37, which returns null, silently excluding the blog post from the site. The same issue affects description: z.string().optional() — writing description: with no value in frontmatter also triggers a validation failure.

Triggering frontmatter example
---
title: "My Post"
date: "2024-01-01"
author: "Me"
published: true
tags:
---

gray-matter parses tags as null. The chain .optional().default([]) only substitutes [] when the input is undefined, not null, so null reaches the union schema and fails.

Prompt for agents
In apps/website/app/(home)/blog/blogSchema.ts, the tags and description fields need to handle null values from YAML frontmatter. In YAML, a key with no value (e.g. tags: on its own line) parses as null, not undefined. Zod's .optional() only accepts undefined, so null causes validation to fail, which silently drops the blog post in processBlogFile (readBlogs.tsx:37).

For the tags field (line 13), consider adding .nullable() before .optional(), or using z.preprocess to coerce null to undefined. For example:
  tags: BlogTagsSchema.nullable().optional().default([])
or
  tags: z.preprocess((v) => v ?? undefined, BlogTagsSchema.optional().default([]))

Similarly, for the description field (line 12), z.string().optional() should become z.string().nullable().optional() to handle the case where description: is present in frontmatter with no value.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant