Migrating a React Blog From Mock Data to a File-Based System

Migrating a React Blog From Mock Data to a File-Based System

React
MDX

Building a modern blog is a journey of solving a series of interesting problems. This post is a straightforward summary of the challenges faced and solutions implemented while migrating a React blog from a single mock-data file to a scalable, file-based architecture using Vite and MDX.

Problem 1: The Monolithic Mock File

  • Problem: Initially, all blog posts were hardcoded in a single JavaScript array (mockBlogPosts). This was difficult to manage, not scalable, and mixed content directly with application code.
  • Solution: Adopt a "one post, one file" architecture. Each post was migrated to its own .mdx file within a src/data/blog/ directory. Metadata like the title and date was stored as frontmatter.

Problem 2: Reading Local Files in Vite

  • Problem: Unlike a backend server, a client-side application built with Vite cannot use Node.js's fs module to read files from the file system. A different approach is needed.
  • Solution: Leverage Vite's native features. By using import.meta.glob in our utils/blog.ts file, we could instruct Vite to find and import all .mdx files at build time, making their content and frontmatter available to the application.

Problem 3: The Stubborn MDX Parsing Error

  • Problem: The MDX parser (@mdx-js/rollup) would fail with an acorn syntax error when it encountered TypeScript code containing generics (e.g., React.FC<Props>) inside a code block. It misinterpreted the TypeScript as invalid JSX.
  • Solution: Instead of complex configuration changes, the most reliable fix was to slightly alter the code inside the .mdx file to remove the ambiguity. Changing React.FC<Props> to a function with an explicit return type ((props: Props): JSX.Element) resolved the parsing issue entirely.

Problem 4: Code Blocks Without Syntax Highlighting

  • Problem: Once the parsing errors were fixed, the content rendered correctly, but all code blocks were monochrome and difficult to read.
  • Solution: We implemented react-syntax-highlighter. The key was to create a custom MDXComponents.tsx file that maps the code tag to a component that uses <SyntaxHighlighter>. This map was then passed as a prop to our rendered MDX content (<Content components={...} />) on the blog post detail page.

Problem 5: Handling Asynchronous Data

  • Problem: The new getBlogPosts() function became async because it relied on Vite's dynamic imports. Calling it synchronously in the UI components (Index.tsx, Blog.tsx) resulted in the error ...slice is not a function, because the code was trying to slice a Promise instead of an array.
  • Solution: Refactor the UI components to use the useEffect and useState hooks. The components now initialize with an empty state and fetch the blog posts asynchronously inside useEffect, populating the state once the data arrives.

Final Result

By systematically addressing these five challenges, the blog's architecture was successfully transformed into a modern, clean, and developer-friendly system where content and code are completely separate.