
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 asrc/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 ourutils/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 anacorn
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. ChangingReact.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 customMDXComponents.tsx
file that maps thecode
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 becameasync
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 aPromise
instead of an array. - Solution: Refactor the UI components to use the
useEffect
anduseState
hooks. The components now initialize with an empty state and fetch the blog posts asynchronously insideuseEffect
, 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.