How to create a blog using React, Remix and MDX

Looking to create a blog using React, Remix and MDX? You're in the right place!

In this tutorial, I'll guide you through the process of building a blog from scratch using these powerful tools. Whether you're a seasoned developer or just starting out, I'll show you how to set up your app, create and customize pages, and deploy your blog to the web. Let's get started!

The Stack


MDX is a markup language that allows you to write content in a mix of Markdown and JSX. This makes it easy to create rich and dynamic blog posts with interactive elements, such as embedded videos, interactive code snippets, and more. You can also export metadata from your MDX files and use it in your application.


Remix is a framework focused on web standards and modern web app UX for building web applications that allows you to create a great user experience with minimal effort.

It's important to mention that Remix has built-in support for MDX.

Tailwind CSS

Tailwind CSS is a popular CSS framework that provides a powerful set of pre-designed styles and utilities that you can use to customize the look and feel of your app.

Using Remix and Tailwind CSS provide a modern and efficient development workflow that can help you create a blog that is both beautiful and highly functional.


Highlight.js provides an easy and efficient way to highlight and format code snippets in your blog posts.

Code snippets are a great way to illustrate technical concepts, explain programming solutions or provide examples to your readers. So, if you are not doing any of these things, feel free to skip it!


Step 1: Create a Remix app

  1. Create a new Remix app by running the following command:
npx create-remix@latest my-blog

Step 2: Install Tailwind CSS

Since there are different ways to install Tailwind CSS depending of your Remix version

  1. Install Tailwind CSS by following the official's guide.

  2. Install the @tailwindcss/typography plugin:

npm i @tailwindcss/typography
  1. Add the plugin to your tailwind.config.js:
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./app/**/*.{js,jsx,ts,tsx}'],
  darkMode: 'class',
  theme: {
    extend: {},
  plugins: [
    require('@tailwindcss/typography'), // <-- add this line

Step 3: Create your blog route.

  1. Create a folder under app/routes to store your blog posts using mdx files. Let's call it blog.
cd app/routes && mkdir blog
  1. Create a layout route by adding a file with the same name of the folder we just created.

We called our folder blog, so, the file is going to be called blog.tsx.

At this point, we might have something like this:

├── app
│   ├── routes
│       ├── blog
│           └── my-first-blog.mdx
│       ├── blog.tsx
│       └── index.tsx
│   ├── root.tsx
│   └── tailwind.css
├── public
│   └── ...
├── package.json
├── remix.config.js
├── tailwind.config.js
└── ...
  1. Add the following code to your layout route.
// app/routes/blog.tsx
import { Outlet } from '@remix-run/react';

export default function Blog() {
  return <Outlet />;
  1. Add Tailwind prose to your layout route.
// app/routes/blog.tsx

// ...
export default function Blog() {
  return (
    <article className="prose dark:prose-invert">
      <Outlet />

Step 4: Setup the Syntax Highlighting (Optional)

As I mentioned before, this section is optional. If you don't need to add syntax highlighting to your blog posts, feel free to skip it.

How it works

If you are still here, let me explain how this works. (I will try to keep it simple)

Internally MDX uses remark and rehype.

  • Remark is a tool that transforms markdown with plugins. These plugins can parse code, transform HTML elements, change syntax, extract frontmatter, and more.

  • Rehype is a tool that transforms HTML with plugins. These plugins let you manipulate, sanitize, compile and configure all types of data, elements and content.

So, we are going to use a rehype plugin to highlight the code snippets in our blog posts.

  1. Install the rehype-highlight package:
npm i rehype-highlight
  1. Add it to your remix.config.js:
/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
  // ...

  // can be an sync / async function or an object
  mdx: async (filename) => {
    const [rehypeHighlight] = await Promise.all([
      import('rehype-highlight').then((mod) => mod.default),

    return {
      rehypePlugins: [rehypeHighlight],
  1. Add the syntax highlighting styles to your layout route. Choose the style you like from here.
// app/routes/blog.tsx
import { Outlet } from '@remix-run/react';
import styles from 'highlight.js/styles/github-dark-dimmed.css'; // replace with the style you like
import type { LinksFunction } from '@remix-run/node';

export const links: LinksFunction = () => {
  return [{ rel: 'stylesheet', href: styles }];

export default function Blog() {
  return (
    <article className="prose dark:prose-invert">
      <Outlet />