How This Website Is Built Using Next.js: A Detailed Beginner's Guide

Hello everyone,
Welcome to this detailed guide on how our website is built using Next.js. I am going to walk you through step-by-step, explaining every tiny detail of the process in plain, simple language. If you are new to Next.js, Tailwind CSS, and MDX, stick around as I share our journey in building a personal blog step by step.


Introduction

In this guide, we will cover:

  • An overview of Next.js and why we chose it.
  • Tailwind CSS configuration and how it boosts rapid UI development.
  • Using MDX for writing blog posts in a flexible way.
  • How our project is organized, with liberally placed code snippets.
  • Deployment, best practices, and a few tips for beginners.

By the end of this blog, you will have a deeper understanding of how modern websites are built and powered with Next.js along with MDX. Let’s begin our journey!


1. Getting Started With Next.js

What is Next.js?

Next.js is a powerful React framework that comes with many built-in features such as:

  • Server-Side Rendering (SSR): Improves performance and SEO.
  • Static Site Generation (SSG): Generates static HTML files at build time.
  • File-based routing: Simplifies routing by following a file-based convention.
  • Built-in optimizations: Image optimization, TypeScript support, and more.

Next.js is especially great for blogs and content-rich websites because it allows us to write content and code in the same place using MDX.


2. Project Setup

Before diving into code, let’s understand how the project is organized. In our repository at /C:/Users/amanr/OneDrive/Desktop/mdx-blog, we have the following major components:

  • tailwind.config.ts: Customizes our Tailwind CSS setup.
  • package.json: Lists the dependencies and scripts.
  • next.config.mjs: Configures Next.js with MDX support.
  • lib/: Contains helper code for MDX and syntax highlighting.
  • app/: Holds our React components for different pages.
  • content/posts/: Contains our MDX blog posts used as content.

Each file and directory has a specific role in building a modern website.


Installing Dependencies

To get started, you need to install dependencies using npm or yarn. Here’s how:

  1. Install Node.js:
    Ensure you have Node.js installed on your machine. You can download it from the Node.js official website.

  2. Initialize the Project:
    Open your terminal and navigate to your project directory. Run the following command to initialize a new Next.js project:

    npx create-next-app@latest mdx-blog --typescript --tailwind --eslint
    cd mdx-blog

    This command sets up a new Next.js project with TypeScript, Tailwind CSS, and ESLint preconfigured.

  3. Install Required Dependencies:
    Next, install the dependencies needed for MDX, syntax highlighting, and other utilities:

    npm install @mdx-js/loader @mdx-js/react @next/mdx next-mdx-remote
    npm install rehype-pretty-code shiki remark-gfm gray-matter
    npm install @tailwindcss/typography react-icons

    Here’s what each dependency does:

    • @mdx-js/loader and @mdx-js/react: Enable MDX support.
    • @next/mdx: Integrates MDX with Next.js.
    • next-mdx-remote: Allows rendering MDX content from remote sources.
    • rehype-pretty-code and shiki: Provide syntax highlighting.
    • remark-gfm: Adds support for GitHub Flavored Markdown.
    • gray-matter: Parses front matter (metadata) in MDX files.
    • @tailwindcss/typography: Adds typography utilities for Tailwind CSS.
    • react-icons: Provides a collection of icons for your project.

3. Configuring the Environment

Tailwind CSS Configuration

Tailwind CSS is a utility-first CSS framework that makes styling your website quick and easy. To customize Tailwind, create a tailwind.config.ts file:

import type { Config } from "tailwindcss";
 
const config: Config = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  darkMode: "class", // Enable dark mode using class-based toggling
  theme: {
    extend: {
      typography: {
        DEFAULT: {
          css: {
            maxWidth: "none",
            color: "inherit",
          },
        },
      },
    },
  },
  plugins: [require("@tailwindcss/typography")], // Add typography plugin
};
 
export default config;

This configuration:

  • Specifies the files where Tailwind should look for classes.
  • Enables dark mode using a class-based approach.
  • Extends the default typography styles.

MDX Configuration

To enable MDX support in Next.js, configure the next.config.mjs file:

import createMDX from '@next/mdx';
import remarkGfm from 'remark-gfm';
import rehypePrettyCode from 'rehype-pretty-code';
 
const withMDX = createMDX({
  options: {
    remarkPlugins: [remarkGfm], // Add GitHub Flavored Markdown support
    rehypePlugins: [
      [rehypePrettyCode, { theme: 'github-dark' }], // Enable syntax highlighting
    ],
  },
});
 
/** @type {import('next').NextConfig} */
const nextConfig = {
  pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'], // Allow MDX files as pages
};
 
export default withMDX(nextConfig);

This configuration:

  • Adds support for GitHub Flavored Markdown.
  • Enables syntax highlighting using the github-dark theme.
  • Allows MDX files to be treated as pages.

4. Writing MDX Content

MDX combines the simplicity of Markdown with the power of React components. Here’s how to structure your blog posts:

  1. Create a Blog Post:
    Inside the content/posts/ directory, create a new file, e.g., my-first-post.mdx:

    ---
    title: "My First Blog Post"
    date: "2023-10-01"
    description: "An example MDX blog post"
    ---
     
    # Welcome to My Blog
     
    You can use **Markdown** syntax normally.
     
    import { MyComponent } from '../components/MyComponent'
     
    <MyComponent />
     
    ## Code Examples
     
    ```javascript
    const greeting = "Hello, World!";
    console.log(greeting);

    And continue writing in Markdown...

  2. Render the Post:
    In your app/posts/[slug]/page.tsx file, create a dynamic route to render the MDX content:

    import { MDXRemote } from 'next-mdx-remote';
    import fs from 'fs';
    import path from 'path';
    import matter from 'gray-matter';
     
    export async function generateStaticParams() {
      const postsDirectory = path.join(process.cwd(), 'content/posts');
      const filenames = fs.readdirSync(postsDirectory);
     
      return filenames.map((filename) => ({
        slug: filename.replace(/\.mdx$/, ''),
      }));
    }
     
    export default async function Post({ params }: { params: { slug: string } }) {
      const filePath = path.join(process.cwd(), 'content/posts', `${params.slug}.mdx`);
      const fileContent = fs.readFileSync(filePath, 'utf8');
      const { content, data } = matter(fileContent);
     
      return (
        <article>
          <h1>{data.title}</h1>
          <p>{data.date}</p>
          <MDXRemote source={content} />
        </article>
      );
    }

    This code:

    • Reads the MDX file from the content/posts/ directory.
    • Parses the front matter (metadata) using gray-matter.
    • Renders the content using MDXRemote.

5. Code Highlighting with Shiki

To add syntax highlighting to your code blocks, configure Shiki in your project:

  1. Create a Highlighter Utility:
    Inside the lib/ directory, create a pretty-code.ts file:

    import { getSingletonHighlighter } from 'shiki/bundle/web';
     
    const highlighterCache = new Map();
     
    async function getHighlighterInstance() {
      if (!highlighterCache.has("default")) {
        const highlighter = await getSingletonHighlighter({
          themes: ['nord'],
          langs: ['javascript', 'typescript', 'jsx', 'tsx'],
        });
        highlighterCache.set("default", highlighter);
      }
      return highlighterCache.get("default");
    }
     
    export default getHighlighterInstance;
  2. Use the Highlighter in MDX:
    Modify your next.config.mjs to use the highlighter:

    import rehypePrettyCode from 'rehype-pretty-code';
     
    const withMDX = createMDX({
      options: {
        rehypePlugins: [
          [rehypePrettyCode, { theme: 'nord' }], // Use the 'nord' theme
        ],
      },
    });

    This setup provides:

    • Syntax highlighting for multiple languages.
    • Dark/light theme support.
    • Cached highlighter instance for better performance.

6. Deployment and Optimization

Deployment

Deploy your site on platforms like Vercel for a frictionless experience. Vercel offers:

  • Serverless functions: Automatically scales your backend.
  • Automatic scaling: Handles traffic spikes effortlessly.
  • Easy rollbacks: Revert to previous deployments with a single click.

To deploy:

  1. Push your code to a GitHub repository.
  2. Connect the repository to Vercel.
  3. Vercel will automatically build and deploy your site.

Performance Optimization

Use Next.js features like Static Site Generation (SSG) and server-side rendering (SSR) to optimize loading times and SEO. For example:

export async function generateStaticParams() {
  const postsDirectory = path.join(process.cwd(), 'content/posts');
  const filenames = fs.readdirSync(postsDirectory);
 
  return filenames.map((filename) => ({
    slug: filename.replace(/\.mdx$/, ''),
  }));
}

This function generates static pages for all blog posts at build time, improving performance.


7. Future Improvements

Here are some ideas for further enhancements:

  • Add Comments: Integrate a commenting system like Disqus or build your own.
  • Dark/Light Theme Toggle: Allow users to switch between themes.
  • Search Functionality: Add a search bar to help users find posts quickly.
  • Analytics: Track user behavior using tools like Google Analytics or Plausible.

Conclusion

Building this website with Next.js and MDX allowed us to combine powerful development features with a flexible content workflow. With Tailwind CSS, we achieved rapid styling and customization, while MDX enabled us to write engaging blog posts enriched with interactive elements. Whether you're a beginner or an experienced developer, this approach offers a modern, scalable solution to creating content-rich sites.

Happy coding! 🚀


If you have any questions or need further assistance, feel free to reach out in the comments below. Don’t forget to share this guide with your friends who are just starting their journey with Next.js!