How this site is built

How this site is built

This site uses Vite and Remark plugins that let you write MDX routes in your Remix app. These MDX routes are transformed to JS at build-time.

Server-side utilities query the frontmatter of each MDX file so that pages can display lists of posts.

The best part is that Remix has your back as your app gets more complex. Need to grab data your database? It's just Remix, so use a loader. Remix let's you use the same mental model and the same tools all the way from a simple blog to a complex app. No rearchitecting is required.

Plugins

MDX support in Vite is provided by @mdx-js/rollup. Frontmatter support in Remark is provided by remark-frontmatter and remark-mdx-frontmatter.

Check out vite.config.ts to see how these are wired together.

MDX routes

To take advantage of nested routes, posts are under /blog/ so that the blog.tsx parent route can handle layout and styling uniformly for all posts.

The about page is a one-off route so it handles its own styling. There are probably other ways to handle styling without needing a surrounding <div />, but that seemed the simplest for now. It is still a nested route relative to the root route, so it gets the site layout for free.

Querying MDX routes

The home page / and blog archive /blog each want to display a subset of the posts. Specifically, they want to display metadata about each included post.

The naive solution would be to read the files directly, say with fs.readFileSync and then parse the frontmatter. There are a couple issues with this approach. For one, the MDX routes are routes not content and they will be built to a different location. Also, if you were using any other plugins for MDX or frontmatter, you'd have to duplicate that setup to parse the posts.

Luckily, Vite's glob imports makes this easy. But relying on Vite, it handles transforming the MDX routes with the same transformation pipeline that is setup in vite.config.ts.

Importantly, all frontmatter queries run behind a loader so that only the data needed by each page is sent over the network. Otherwise, chunks for all posts would get sent to the browser even if a route like the home page only wanted to display metadata for a subset of the posts.

Search Functionality

This site implements a powerful and user-friendly search feature that enhances the overall user experience. Here's how it works:

  1. Real-time Search: As users type in the search box, the results update dynamically. This instant feedback helps users quickly find the content they're looking for without waiting for page reloads.

  2. Debounced Queries: To optimize performance and reduce unnecessary server requests, the search function uses a debounce mechanism. This means that the search only triggers a short delay after the user stops typing, preventing excessive API calls and improving overall site responsiveness.

  3. Server-side Processing: The search queries are processed on the server-side using Remix loaders. This approach ensures that the search is fast and efficient, even with a large number of blog posts or complex search algorithms.

  4. Frontmatter Metadata: The search functionality leverages the frontmatter metadata in each MDX file. This allows for searching not just the content of the posts, but also specific attributes like titles, descriptions, and custom tags.

  5. SEO-Friendly URLs: As users search, the URL updates to reflect their query. This creates shareable, SEO-friendly links to specific search results, improving discoverability and user engagement.

  6. Accessibility: The search feature is built with accessibility in mind, ensuring that it can be easily used with keyboard navigation and screen readers.

Search query example

By implementing this efficient and user-centric search functionality, the site provides a seamless experience for visitors looking for specific content or exploring the blog's offerings. This approach not only improves user satisfaction but also contributes to better SEO performance by making content more discoverable and engaging.

Alternatives

This approach handles MDX at build-time. For some use-cases, you might prefer to handle MDX at runtime. It's a bit more work since you have to setup a Unified pipeline yourself, but it let's you model posts as content rather than routes. That is, you would have a blog.$slug.tsx route that would dynamically load the MDX content for that post.

There are different trade-offs here, so its up to you to decide which one is better. But the approach this site takes is often a simpler starting point.


Thank you for reading! I hope you found this post informative and helpful.

If you have any questions or feedback, feel free to reach out to me on Twitter. I always appreciate hearing from readers and am happy to help with any queries.

Stay tuned for more posts, and happy coding!