DEV Community

Cover image for Building a Modern Blog with Next.js, MDX, and Tailwind CSS
gerry leo nugroho
gerry leo nugroho

Posted on

Building a Modern Blog with Next.js, MDX, and Tailwind CSS

Why stress over complicated setups when you can use tools that make blogging smooth and fun? Next.js, MDX, and Tailwind CSS are a dream team for building a blog that’s both powerful and stylish at the same time. Next.js handles the backend and routing, MDX lets you create dynamic and interactive posts, and Tailwind CSS gives you a clean, customizable design system.

Together, they let you focus on creating content while keeping your blog fast, flexible, and visually appealing. Plus, features like clean typography and light/dark mode options aren’t just nice-to-haves—they make your blog more engaging and user-friendly. Whether you're just starting out or planning to grow, this stack has you covered. Ready to get started? Let’s build something awesome.


1. Why This Tech Stack?

Gemika Haziq Nugroho - Gerry Leo Nugroho - Animation - 02

Picking the right tools is crucial for building a great blog. The combo of Next.js, MDX, and Tailwind CSS delivers a smooth, high-performance, and flexible setup. Here’s why it works:

- Next.js: The Backbone of Your Blog

Next.js simplifies web development with server-side rendering (SSR) and static site generation (SSG), ensuring fast load times and better SEO. Its file-based routing makes content organization a breeze, and built-in optimizations let you focus on content, not performance.

- MDX: Where Content Meets Interactivity

MDX enhances Markdown by letting you embed React components directly into posts. Add interactive charts, buttons, or custom elements effortlessly, making your blog both informative and engaging.

- Tailwind CSS: Design Without Limits

Tailwind CSS uses a utility-first approach, enabling quick, beautiful designs. Style elements directly in your markup—whether it’s clean typography or dark mode. Its theming keeps your blog consistent and uniquely yours.

- Focus on Content, Not Complexity

This stack lets you focus on content. Next.js handles performance, MDX enables dynamic posts, and Tailwind ensures a polished design. Together, they create a fast, flexible, and future-proof blog.


2. Setting Up Your Development Environment

Gemika Haziq Nugroho - Gerry Leo Nugroho - Animation - 01

Before diving into the exciting part of building your blog, let’s make sure your development environment is ready to go. This section will guide you through setting up the foundational tools and dependencies needed to kickstart your project. Don’t worry—it’s easier than it sounds!

Step 1: Install Node.js and a Package Manager

To get started, you’ll need Node.js, which includes npm (Node Package Manager) by default. Alternatively, you can use Yarn or pnpm if you prefer a faster or more efficient package manager.

  • Install Node.js: Head over to the official Node.js website and download the latest stable version. During installation, ensure that npm is also installed.
  • Optional: If you’d rather use Yarn or pnpm, install them globally using the following commands:
npm install -g yarn   # For Yarn
npm install -g pnpm   # For pnpm
Enter fullscreen mode Exit fullscreen mode

Once installed, verify your setup by running:

node -v && npm -v
Enter fullscreen mode Exit fullscreen mode

This should display the versions of Node.js and npm (or your chosen package manager).

Step 2: Create a New Next.js Project

With Node.js in place, it’s time to scaffold a new Next.js project. Open your terminal and run the following command:

npx create-next-app@latest my-blog
Enter fullscreen mode Exit fullscreen mode

Replace my-blog with your desired project name. You’ll be prompted with a few questions about TypeScript, ESLint, and other configurations. For now, feel free to accept the defaults or customize based on your preferences.

Once the setup is complete, navigate into your project directory:

cd my-blog
Enter fullscreen mode Exit fullscreen mode

At this point, you can test your new Next.js app by running:

npm run dev
Enter fullscreen mode Exit fullscreen mode

Visit http://localhost:3000 in your browser, and you should see the default Next.js welcome page. Success!

Step 3: Add Dependencies for MDX and Tailwind CSS

Now that your Next.js project is up and running, let’s add the necessary dependencies for MDX and Tailwind CSS.

  1. Install MDX Support

To enable MDX in your project, install the required packages:

npm install @next/mdx @mdx-js/loader
Enter fullscreen mode Exit fullscreen mode

These packages allow Next.js to process .mdx files seamlessly.

  1. Install Tailwind CSS

Tailwind CSS requires a bit more setup, but don’t worry—it’s straightforward. Run the following command to install Tailwind and its peer dependencies:

npm install tailwindcss postcss autoprefixer
Enter fullscreen mode Exit fullscreen mode

Next, initialize Tailwind’s configuration files:

npx tailwindcss init
Enter fullscreen mode Exit fullscreen mode

This will generate two files: tailwind.config.js and postcss.config.js. We’ll configure these later.

Step 4: Verify Your Setup

Your project now has all the core dependencies installed. Here’s a quick recap of what we’ve done so far:

  • Installed Node.js and a package manager.
  • Created a new Next.js project.
  • Added support for MDX and Tailwind CSS.

You’re ready to move on to the next steps, where we’ll configure MDX and Tailwind CSS to work together harmoniously. But before that, take a moment to commit your initial setup to Git (if you’re using version control):

git init
git add .
git commit -m "Initial setup with Next.js, MDX, and Tailwind CSS"
Enter fullscreen mode Exit fullscreen mode

With the foundation laid, you’re well on your way to building a modern, feature-rich blog. Let’s keep going!


3. Configuring MDX for Dynamic Content

Gemika Haziq Nugroho - Gerry Leo Nugroho - Animation - 02

Now that your project is set up, let’s dive into configuring MDX to unlock its full potential. MDX allows you to combine the simplicity of Markdown with the power of React components, enabling you to create dynamic and interactive blog posts. Here’s how to get started.

Step 1: Configure next.config.js for MDX Support

To enable .mdx files in your Next.js app, you’ll need to update the next.config.js file. Open the file and modify it to include the following configuration:

Here’s the code with comments added to explain its functionality:

// Import the `@next/mdx` package, which allows Next.js to handle MDX files (Markdown with JSX).
// The `withMDX` function is initialized with a configuration object that specifies the file extensions
// it should process (in this case, `.md` and `.mdx` files).
const withMDX = require('@next/mdx')({
  extension: /\.mdx?$/, // Regular expression to match `.md` and `.mdx` files
});

// Export the configuration for Next.js, enhanced with MDX support.
// The `withMDX` function wraps the Next.js configuration and adds MDX support.
module.exports = withMDX({
  // Specify the file extensions that Next.js should consider as pages.
  // This includes standard JavaScript/TypeScript files (`js`, `jsx`, `ts`, `tsx`)
  // as well as Markdown and MDX files (`md`, `mdx`).
  pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'],
});
Enter fullscreen mode Exit fullscreen mode

Explanation:

  1. @next/mdx: This is a Next.js plugin that enables MDX support, allowing you to write Markdown with embedded JSX components.
  2. extension: /\.mdx?$/: This regular expression ensures that both .md and .mdx files are processed by the plugin.
  3. pageExtensions: This configuration tells Next.js which file extensions should be treated as pages. By including md and mdx, you can create pages using Markdown or MDX files alongside traditional JavaScript/TypeScript files.

This setup is useful for projects that want to leverage the simplicity of Markdown for content while still using the power of React components within those files.

Step 2: Create an Example Blog Post in MDX Format

With MDX configured, let’s create your first blog post. Inside the pages directory, create a new folder called posts to organize your content. Then, add a file named my-first-post.mdx with the following content:

---
title: "My First Blog Post"
date: "2023-10-01"
---

# Welcome to My Blog

This is my very first post written in **MDX**! MDX allows you to write Markdown while embedding React components for dynamic content. 

Here’s an example of a styled button:

<Button>Click Me!</Button>

And here’s some code for good measure:

`console.log("Hello, world!")`

Isn’t this amazing? You can mix static content with interactive elements effortlessly.
Enter fullscreen mode Exit fullscreen mode

Notice how we’ve embedded a <Button> component directly in the Markdown. This is where MDX truly shines—it lets you bring your content to life.

Step 3: Embed React Components Within Markdown

To make the <Button> component work, you’ll need to define it. Create a new folder called components in the root of your project, and inside it, add a file named Button.js with the following code:

// Define a functional component named `Button` that accepts `children` as a prop.
// `children` represents the content that will be rendered inside the button.
export default function Button({ children }) {
  return (
    // Render a button element with the following Tailwind CSS classes:
    // - `bg-blue-500`: Sets the background color to a shade of blue.
    // - `text-white`: Sets the text color to white.
    // - `px-4`: Adds horizontal padding of 4 units (16px).
    // - `py-2`: Adds vertical padding of 2 units (8px).
    // - `rounded`: Applies rounded corners to the button.
    // - `hover:bg-blue-600`: Changes the background color to a darker shade of blue on hover.
    <button className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
      {/* Render the `children` inside the button */}
      {children}
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

Key Points:

  1. Reusable Component: This Button component is reusable and can be used anywhere in the application by passing different content as children.
  2. Styling with Tailwind CSS: The button is styled using Tailwind CSS utility classes, making it easy to customize and maintain.
  3. Hover Effect: The hover:bg-blue-600 class adds an interactive hover effect to the button.

Next, import the Button component into your MDX file by adding this line at the top of my-first-post.mdx:

import Button from '../../components/Button'
Enter fullscreen mode Exit fullscreen mode

You can use this component like this:

<Button>Click Me</Button>
<Button>Submit</Button>
Enter fullscreen mode Exit fullscreen mode

Now, when you run your app and navigate to /posts/my-first-post, you’ll see your blog post rendered with a fully functional button. This approach opens up endless possibilities—embed charts, forms, or even custom widgets to enhance your content.

Why Embedding React Components Matters

The ability to embed React components within Markdown is a game-changer. Imagine creating tutorials where readers can interact with live code examples, or embedding visually appealing charts to illustrate data points. With MDX, your blog becomes more than just a collection of static pages—it’s an interactive experience that keeps readers engaged.

By combining Markdown’s simplicity with React’s flexibility, MDX empowers you to craft content that’s both informative and dynamic. Whether you’re writing technical guides, sharing personal stories, or showcasing projects, this setup ensures your blog stands out.


4. Designing with Tailwind CSS

Gemika Haziq Nugroho - Gerry Leo Nugroho - Animation - 04

With MDX configured and your content ready to shine, it’s time to focus on the visual aspect of your blog. Tailwind CSS provides a powerful utility-first framework that lets you design beautiful, responsive layouts with ease. In this section, we’ll walk through setting up Tailwind, customizing its configuration for a unique look, and implementing features like clean typography and dark mode.

Step 1: Configure tailwind.config.js

The tailwind.config.js file is where you define your design system. Open the file and customize it to suit your blog’s aesthetic. Here’s an example configuration:

Here’s a commented version of the provided JavaScript code:

module.exports = {
  // Define the files to be processed by Tailwind CSS
  content: [
    './pages/**/*.{js,ts,jsx,tsx,mdx}', // Include all JS/TS/JSX/TSX/MDX files in the 'pages' directory
    './components/**/*.{js,ts,jsx,tsx,mdx}', // Include all JS/TS/JSX/TSX/MDX files in the 'components' directory
  ],
  // Customize the theme
  theme: {
    extend: {
      // Add custom colors
      colors: {
        primary: '#1E3A8A', // A deep blue color
        secondary: '#FBBF24', // A golden yellow color
        dark: '#1F2937', // A dark gray color
        light: '#F9FAFB', // A light gray color
      },
      // Define custom font families
      fontFamily: {
        sans: ['Inter', 'sans-serif'], // Use Inter as the default sans-serif font
        serif: ['Merriweather', 'serif'], // Use Merriweather as the default serif font
      },
      // Add custom spacing values
      spacing: {
        '18': '4.5rem', // Add a custom spacing value of 4.5rem
      },
    },
  },
  // No additional plugins are included
  plugins: [],
  // Enable dark mode using the 'class' strategy
  darkMode: 'class',
};
Enter fullscreen mode Exit fullscreen mode

This code is a configuration file for Tailwind CSS, a utility-first CSS framework. It specifies the content to be processed, customizes the theme (colors, fonts, spacing), and enables dark mode using a class-based strategy.

Step 2: Enhance Readability with Clean Typography

Typography plays a crucial role in making your blog easy to read and visually appealing. Tailwind’s utility classes make it simple to fine-tune your text styles. Here are some tips for creating clean typography:

  • Font Choices: Use a sans-serif font like Inter for body text to ensure readability. For headings, consider a serif font like Merriweather to add elegance.
  • Line Height and Spacing: Adjust line heights and letter spacing for optimal legibility. For example:
  <h1 class="text-4xl font-serif leading-tight tracking-wide">Welcome to My Blog</h1>
  <p class="text-lg font-sans leading-relaxed">This is a sample paragraph with clean typography.</p>
Enter fullscreen mode Exit fullscreen mode
  • Responsive Scaling: Use responsive utilities to adapt font sizes for different screen sizes:
<p class="text-base sm:text-lg md:text-xl lg:text-2xl">Scaling text for all devices.</p>
Enter fullscreen mode Exit fullscreen mode

These small tweaks can significantly improve the reading experience, ensuring your content looks polished on any device.

Step 3: Implement Dark Mode

Dark mode has become a must-have feature for modern websites, and Tailwind makes it effortless to implement. With the darkMode: 'class' setting in your tailwind.config.js, you can toggle dark mode by adding or removing the dark class on the <html> or <body> element.

Here’s how to style your blog for both light and dark modes:

<div class="bg-light dark:bg-dark text-dark dark:text-light">
  <h1 class="text-3xl font-bold">Hello, World!</h1>
  <p class="text-lg">This text adapts to light and dark modes seamlessly.</p>
</div>
Enter fullscreen mode Exit fullscreen mode

To toggle dark mode dynamically, you can use JavaScript to add or remove the dark class:

/**
 * Toggles dark mode on the webpage by adding or removing a 'dark' class 
 * from the document's root element (<html>).
 * 
 * This function checks if the 'dark' class is currently applied to the document.
 * If it is, the class is removed, switching the page to light mode.
 * If it isn't, the class is added, enabling dark mode.
 * 
 * Usage:
 * - Call this function when a user clicks a dark mode toggle button or performs
 *   any other action to switch between light and dark themes.
 * 
 * Example:
 * <button onclick="toggleDarkMode()">Toggle Dark Mode</button>
 * 
 * Note:
 * - Ensure your CSS is set up to style elements differently based on the presence
 *   of the 'dark' class on the <html> element.
 *   Example CSS:
 *   .dark { background-color: #1a1a1a; color: #ffffff; }
 */
function toggleDarkMode() {
  // Toggle the 'dark' class on the <html> element
  document.documentElement.classList.toggle('dark');
}
Enter fullscreen mode Exit fullscreen mode

Add a button to your blog’s layout for users to switch modes:

<!-- 
  Button to toggle dark mode on and off.
  - `onclick="toggleDarkMode()"`: Calls the `toggleDarkMode` JavaScript function when clicked.
  - `class="px-4 py-2 bg-primary text-light rounded"`: Applies Tailwind CSS classes for styling:
    - `px-4`: Adds horizontal padding of 1rem (16px) on both sides.
    - `py-2`: Adds vertical padding of 0.5rem (8px) on top and bottom.
    - `bg-primary`: Sets the background color to a custom primary color (defined in your Tailwind config).
    - `text-light`: Sets the text color to a light color (e.g., white or a light gray).
    - `rounded`: Adds rounded corners to the button for a softer look.
-->
<button onclick="toggleDarkMode()" class="px-4 py-2 bg-primary text-light rounded">
  Toggle Dark Mode
</button>
Enter fullscreen mode Exit fullscreen mode

Step 4: Ensure Accessible Contrast Ratios

When designing for dark mode, accessibility is key. Use tools like WebAIM Contrast Checker to ensure your color combinations meet WCAG standards. For example:

  • Text on a dark background should have a contrast ratio of at least 4.5:1.
  • Use Tailwind’s opacity utilities (opacity-75, etc.) to soften colors without compromising readability.

Why Tailwind CSS Makes Design Effortless

By leveraging Tailwind’s utility classes, you can rapidly prototype and refine your blog’s design without writing custom CSS. From clean typography to responsive layouts and dark mode support, Tailwind empowers you to create a visually stunning and user-friendly blog. Whether you’re tweaking spacing, experimenting with colors, or ensuring accessibility, Tailwind’s flexibility ensures your design remains consistent and professional.

With your design system in place, your blog is now ready to captivate readers with its aesthetics and functionality. Let’s move on to structuring your content!


5. Structuring Your Blog

Gemika Haziq Nugroho - Gerry Leo Nugroho - Animation - 05

A well-organized blog is the foundation of a great user experience. In this section, we’ll explore how to structure your blog posts effectively and leverage Next.js’s dynamic routing to fetch and display content seamlessly. By the end of this section, you’ll have a system in place to list all your posts on the homepage and render individual posts dynamically.

Step 1: Organizing Blog Posts

To keep your project tidy, it’s important to establish a clear folder structure for your .mdx files. A common approach is to store all your blog posts in a dedicated directory, such as posts. Here’s an example structure:

/pages
  /posts
    my-first-post.mdx
    another-post.mdx
    yet-another-post.mdx
Enter fullscreen mode Exit fullscreen mode

Each .mdx file represents a single blog post. You can also include metadata (like title, date, and tags) at the top of each file using frontmatter. For example:

---
title: "My First Blog Post"
date: "2023-10-01"
tags: ["nextjs", "mdx", "tailwindcss"]
---

# Welcome to My Blog

This is the content of my first post!
Enter fullscreen mode Exit fullscreen mode

This metadata will later be used to display summaries on the homepage and organize posts by tags or categories.

Step 2: Fetching and Displaying Posts Dynamically

Next.js makes it easy to fetch and display your blog posts dynamically. Let’s start by creating a homepage that lists all your posts.

Listing All Posts on the Homepage

Create a file named index.js inside the pages directory. This will serve as your blog’s homepage. Use getStaticProps to fetch all .mdx files from the posts directory and extract their metadata:

// Import necessary modules
import fs from 'fs'; // File system module to read files
import path from 'path'; // Path module to handle file paths
import matter from 'gray-matter'; // Library to parse Markdown front matter

// Define the Home component, which will render the blog's homepage
export default function Home({ posts }) {
  return (
    <div>
      {/* Blog title */}
      <h1 className="text-4xl font-bold mb-8">My Blog</h1>

      {/* List of blog posts */}
      <ul>
        {/* Map through the posts array and render each post */}
        {posts.map((post, index) => (
          <li key={index} className="mb-6">
            {/* Link to the individual post page */}
            <a href={`/posts/${post.slug}`} className="text-blue-500 hover:underline">
              {/* Post title */}
              <h2 className="text-2xl font-semibold">{post.data.title}</h2>
            </a>
            {/* Post date */}
            <p className="text-gray-600">{post.data.date}</p>
          </li>
        ))}
      </ul>
    </div>
  );
}

// getStaticProps is a Next.js function that runs at build time to fetch data
export async function getStaticProps() {
  // Read the list of files in the 'pages/posts' directory
  const files = fs.readdirSync(path.join('pages/posts'));

  // Process each file to extract post data
  const posts = files.map((filename) => {
    // Remove the '.mdx' extension to get the post slug (URL-friendly name)
    const slug = filename.replace('.mdx', '');

    // Read the content of the Markdown file
    const markdownWithMeta = fs.readFileSync(path.join('pages/posts', filename), 'utf-8');

    // Use gray-matter to parse the front matter (metadata) from the Markdown file
    const { data } = matter(markdownWithMeta);

    // Return an object containing the slug and parsed front matter
    return { slug, data };
  });

  // Return the posts as props to the Home component
  return {
    props: { posts },
  };
}
Enter fullscreen mode Exit fullscreen mode

Explanation of the Code:

  1. Imports:

    • fs: Used to read files from the file system.
    • path: Helps handle file paths in a cross-platform way.
    • gray-matter: Parses Markdown files to extract metadata (front matter).
  2. Home Component:

    • Displays the blog title and a list of posts.
    • Each post is rendered as a link (<a>) to its individual page, with the title and date displayed.
  3. getStaticProps:

    • A Next.js function that runs at build time to fetch data.
    • Reads all .mdx files from the pages/posts directory.
    • Extracts the slug (filename without .mdx) and front matter (metadata like title and date).
    • Passes the processed data as props to the Home component.

This setup ensures that your blog posts are dynamically loaded and displayed on the homepage, while keeping the content management simple with Markdown files.

Rendering Individual Posts Dynamically

To render individual posts dynamically, create a file named [slug].js inside the pages/posts directory. This file uses dynamic routing to fetch and display the content of a specific post based on its slug:

import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote } from 'next-mdx-remote'

const PostPage = ({ source, frontmatter }) => {
  return (
    <div>
      <h1 className="text-4xl font-bold mb-4">{frontmatter.title}</h1>
      <p className="text-gray-600 mb-8">{frontmatter.date}</p>
      <article className="prose lg:prose-xl dark:prose-invert">
        <MDXRemote {...source} />
      </article>
    </div>
  )
}

export async function getStaticPaths() {
  const files = fs.readdirSync(path.join('pages/posts'))
  const paths = files.map((filename) => ({
    params: { slug: filename.replace('.mdx', '') },
  }))

  return {
    paths,
    fallback: false,
  }
}

export async function getStaticProps({ params }) {
  const markdownWithMeta = fs.readFileSync(
    path.join('pages/posts', `${params.slug}.mdx`),
    'utf-8'
  )
  const { data, content } = matter(markdownWithMeta)
  const mdxSource = await serialize(content)

  return {
    props: { source: mdxSource, frontmatter: data },
  }
}

export default PostPage
Enter fullscreen mode Exit fullscreen mode

This setup uses getStaticPaths to generate routes for each post and getStaticProps to fetch the content and metadata for the requested post. The MDXRemote component renders the MDX content dynamically, allowing you to embed React components seamlessly.

Why Dynamic Routing Matters

Dynamic routing ensures your blog scales effortlessly as you add more content. Instead of manually creating pages for each post, Next.js handles everything automatically based on your folder structure and metadata. This not only saves time but also keeps your project organized and maintainable.

By combining a clean folder structure with Next.js’s powerful data-fetching methods, you’ve now created a robust system for managing and displaying your blog posts. Readers can browse your homepage, click on a post, and enjoy a seamless reading experience—all while you focus on writing great content. Let’s move on to enhancing the user experience with additional features!


6. Enhancing User Experience

Gemika Haziq Nugorho - Gerry Leo Nugroho - Animation - 06

A great blog isn’t just about content and design—it’s also about the little details that make the user experience smooth, engaging, and accessible. In this section, we’ll explore additional features you can add to elevate your blog while keeping accessibility at the forefront of your build process.

Syntax Highlighting for Code Blocks

If your blog includes code snippets, syntax highlighting is a must-have feature to improve readability. Libraries like Prism or Shiki integrate seamlessly with MDX and Tailwind CSS. For example, you can use react-syntax-highlighter to style your code blocks dynamically:

// Importing the Prism component from 'react-syntax-highlighter' to handle syntax highlighting
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'

// Importing a specific syntax highlighting theme called 'tomorrow' from the Prism styles
import { tomorrow } from 'react-syntax-highlighter/dist/cjs/styles/prism'

// Defining a functional component called `CodeBlock` that takes two props:
// - `children`: The code content to be highlighted
// - `language`: The programming language of the code for proper syntax highlighting
const CodeBlock = ({ children, language }) => {
  return (
    // Using the `SyntaxHighlighter` component to wrap the code content
    // - `language`: Specifies the language for syntax highlighting (e.g., 'javascript', 'python')
    // - `style`: Applies the 'tomorrow' theme for the syntax highlighting
    // - `className`: Adds a 'rounded-md' class for styling (e.g., rounded corners)
    <SyntaxHighlighter language={language} style={tomorrow} className="rounded-md">
      {children} {/* The code content passed as children */}
    </SyntaxHighlighter>
  )
}

// Exporting the `CodeBlock` component as the default export of this module
export default CodeBlock
Enter fullscreen mode Exit fullscreen mode

Explanation of Key Points:

  1. Imports:
  2. Prism as SyntaxHighlighter: The component used to render syntax-highlighted code.
  3. tomorrow: A pre-defined style theme for syntax highlighting.

  4. Props:

  5. children: The actual code content to be displayed and highlighted.

  6. language: Specifies the programming language for accurate syntax highlighting.

  7. SyntaxHighlighter Component:

  8. Wraps the code content and applies syntax highlighting based on the provided language and style.

  9. Styling:

  10. className="rounded-md": Adds rounded corners to the code block for better visual appeal.

  11. Export:

  12. The CodeBlock component is exported for use in other parts of the application.

SEO Optimization with Metadata Tags

Search engine optimization (SEO) is crucial for driving traffic to your blog. Use Next.js’s built-in <Head> component to add metadata tags like titles, descriptions, and Open Graph images to each page. For example:

import Head from 'next/head';

const PostPage = ({ frontmatter }) => {
  return (
    <>
      <Head>
        {/* Set the page title dynamically using the post's title */}
        <title>{frontmatter.title} | My Blog</title>

        {/* Set the meta description for SEO, defaulting to a generic description if no excerpt is provided */}
        <meta name="description" content={frontmatter.excerpt || 'A blog post about web development'} />

        {/* Open Graph meta tags for social media sharing */}
        <meta property="og:title" content={frontmatter.title} />
        <meta property="og:description" content={frontmatter.excerpt || 'A blog post about web development'} />

        {/* Open Graph image for social media sharing */}
        <meta property="og:image" content="/images/og-image.png" />
      </Head>
    </>
  );
};

export default PostPage;
Enter fullscreen mode Exit fullscreen mode

Key Points:

  1. Dynamic Title: The page title is dynamically set using the frontmatter.title.
  2. SEO Description: The meta description is set using the frontmatter.excerpt, with a fallback to a default description.
  3. Open Graph Tags: These tags are used to optimize how the post appears when shared on social media platforms like Facebook or Twitter.
  4. OG Image: A default Open Graph image is provided for social media sharing.

Social Sharing Buttons and Analytics Integration

Encourage readers to share your posts by adding social sharing buttons. You can use libraries like react-share to create customizable buttons for platforms like Twitter, LinkedIn, and Facebook. For example:

// Importing necessary components from the 'react-share' library
import { TwitterShareButton, LinkedinShareButton } from 'react-share'

// Functional component for rendering share buttons
const ShareButtons = ({ url, title }) => {
  return (
    // Container for the share buttons with flex layout and spacing
    <div className="flex space-x-4 mt-8">
      {/* Twitter share button with the provided URL and title */}
      <TwitterShareButton url={url} title={title}>
        Share on Twitter
      </TwitterShareButton>

      {/* LinkedIn share button with the provided URL and summary (title) */}
      <LinkedinShareButton url={url} summary={title}>
        Share on LinkedIn
      </LinkedinShareButton>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Key Points:

  • The ShareButtons component accepts url and title as props.
  • It uses TwitterShareButton and LinkedinShareButton from the react-share library to enable sharing on Twitter and LinkedIn.
  • The buttons are styled with Tailwind CSS classes (flex, space-x-4, and mt-8) for layout and spacing.

Additionally, integrate analytics tools like Google Analytics or Plausible to track visitor behavior and gain insights into your audience. Use Next.js’s _app.js file to initialize the analytics script globally.

Accessibility Best Practices

Throughout the build process, prioritize accessibility to ensure your blog is inclusive and usable for everyone. Use semantic HTML elements like <header>, <main>, and <footer> to structure your pages logically. Ensure sufficient color contrast for text and interactive elements, and test your site with screen readers to identify potential issues. Tailwind CSS’s utility classes like sr-only and focus-visible can help you implement accessibility features effectively.

By incorporating these enhancements—syntax highlighting, SEO optimization, social sharing buttons, analytics, and accessibility best practices—you’re not just building a blog; you’re creating an experience that delights readers and keeps them coming back for more. Let’s wrap things up with deployment!


7. Deploying Your Blog

Gemika Haziq Nugroho - Gerry Leo Nugroho - Animation - 06

Deploying your blog is the final step in bringing your hard work to life, and with modern tools, it’s easier than ever. Follow this step-by-step guide to get your blog live and take advantage of continuous deployment and automatic builds.

Step 1: Build the Project Locally

Before deploying, ensure your project is ready for production by building it locally. Run the following command in your terminal:

npm run build
Enter fullscreen mode Exit fullscreen mode

This generates an optimized version of your blog in the .next folder. Test the build by running:

npm start
Enter fullscreen mode Exit fullscreen mode

Visit http://localhost:3000 to confirm everything works as expected. If all looks good, you’re ready to move on.

Step 2: Push the Code to a Git Repository

Next, push your code to a Git repository like GitHub, GitLab, or Bitbucket. If you haven’t initialized a repository yet, do so by running:

git init
git add .
git commit -m "Initial commit"
Enter fullscreen mode Exit fullscreen mode

Then, create a new repository on your chosen platform and link it to your local project:

git remote add origin https://github.com/your-username/your-repo-name.git
git branch -M main
git push -u origin main
Enter fullscreen mode Exit fullscreen mode

This ensures your code is safely stored and ready for deployment.

Step 3: Connect the Repository to the Deployment Platform

Most modern deployment platforms integrate seamlessly with Git repositories, enabling automatic builds and deployments whenever you push updates. Navigate to your chosen platform (e.g., Vercel, Netlify, or Cloudflare Pages) and follow these steps:

  1. Sign in to your account and select “New Project” or “Add Site.”
  2. Connect your Git repository by authorizing access to your account.
  3. Choose the repository containing your blog and configure the deployment settings (most platforms detect Next.js projects automatically).
  4. Trigger the first deployment by clicking “Deploy” or pushing a new commit to the main branch.

Once the deployment process completes, your blog will be live, and you’ll receive a URL to share with the world.

Continuous Deployment and Automatic Builds

One of the biggest advantages of this workflow is the ease of continuous deployment. With your repository connected, every time you push changes to the main branch, the platform automatically rebuilds and deploys your blog. This means you can focus on writing and improving your content without worrying about manual updates. Additionally, many platforms offer features like preview environments for pull requests, allowing you to test changes before merging them into production.

By leveraging these tools, you ensure your blog is always up-to-date, performant, and ready to reach your audience. With your blog now live, it’s time to celebrate your achievement and start sharing your ideas with the world!


8. Wrap-up!

Gemika Haziq Nugroho - Gerry Leo Nugroho - Animation - 07

Building a blog with Next.js, MDX, and Tailwind CSS is a game-changer for developers and content creators alike. This powerful stack offers unmatched flexibility, performance, and customization options, enabling you to craft a blog that’s both functional and visually stunning. With Next.js handling routing and optimization, MDX blending dynamic content with Markdown simplicity, and Tailwind CSS providing a utility-first approach to design, you have all the tools you need to create a blog that stands out.

Whether you’re writing technical tutorials, sharing personal stories, or showcasing your projects, this stack empowers you to focus on what matters most—your content. Now is the perfect time to experiment with customizations, explore new features, and make your blog truly unique. Share your creations with the community and inspire others to embark on their own blogging journey. Ready to take the plunge? Start building your own blog today!

9. Bonus Tips (Optional Section)

Gemika Haziq Nugroho - Gerry Leo Nugroho - Animation - 09

Maintaining and scaling your blog is just as important as building it, and with a few strategic tips, you can ensure it remains robust and adaptable as your audience grows. Here are some quick ideas to take your blog to the next level:

Automating Backups

Protect your hard work by setting up automated backups for your content and database (if applicable). Use tools like GitHub Actions or third-party services to schedule regular snapshots of your repository. For added security, consider exporting your .mdx files periodically and storing them in cloud storage solutions like AWS S3 or Google Drive.

Exploring Advanced Tailwind Configurations

Tailwind CSS is incredibly versatile, and diving deeper into its configuration can unlock even more potential for your blog. Experiment with features like custom plugins, responsive design utilities, and advanced color palettes to refine your design system. For example, you can create reusable components using Tailwind’s @apply directive or extend your theme to include unique typography scales that match your brand identity.

Integrating CMS Options for Non-Technical Contributors

If you plan to collaborate with non-technical contributors, integrating a headless CMS like Contentful or Sanity can streamline the content creation process. These platforms allow users to write and manage posts through an intuitive interface while seamlessly connecting to your Next.js app via APIs. By combining the flexibility of MDX with the ease of a CMS, you can empower your team to focus on storytelling without worrying about code.

By implementing these bonus tips, you’ll not only future-proof your blog but also make it easier to manage and scale over time. Whether you’re automating workflows, refining your design, or enabling collaboration, these strategies will help you maintain a polished and professional presence online.

Top comments (0)