Build a Simple Next.js Interactive Markdown Editor

Written by

in

In the world of web development, the ability to create and edit content dynamically is a crucial skill. Markdown, a lightweight markup language, has become a favorite for its simplicity and readability. This tutorial will guide you through building a simple, yet functional, interactive Markdown editor using Next.js, a powerful React framework. We’ll cover everything from setting up your project to implementing features like live preview and basic formatting options. This project is perfect for beginners and intermediate developers looking to expand their knowledge of Next.js and frontend development.

Why Build a Markdown Editor?

Markdown editors are incredibly useful for a variety of tasks. They’re ideal for writing documentation, creating blog posts, taking notes, and even composing emails. The beauty of Markdown lies in its plain-text format, which makes it easy to write and read, while still allowing for rich formatting. Building your own editor provides a hands-on learning experience, allowing you to understand the intricacies of web development while creating a practical tool.

What You’ll Learn

By the end of this tutorial, you’ll have a fully functional Markdown editor. You’ll learn how to:

  • Set up a Next.js project.
  • Implement a text area for Markdown input.
  • Render Markdown to HTML using a library.
  • Create a live preview feature that updates in real-time.
  • Add basic formatting buttons for bold, italic, and headings.
  • Handle user input and state management in React.

Prerequisites

Before you start, make sure you have the following:

  • Node.js and npm (or yarn) installed on your system.
  • A basic understanding of HTML, CSS, and JavaScript.
  • Familiarity with React is helpful, but not strictly required.
  • A code editor (like VS Code) for writing your code.

Setting Up Your Next.js Project

First, let’s create a new Next.js project. Open your terminal and run the following command:

npx create-next-app markdown-editor
cd markdown-editor

This command creates a new Next.js project named “markdown-editor” and navigates you into the project directory. Next.js sets up all the necessary configurations for you, so you can focus on building the editor.

Installing Dependencies

We’ll need a few dependencies for our project:

  • react-markdown: To convert Markdown text to HTML.
  • remark-gfm: A plugin for `react-markdown` to support GitHub Flavored Markdown (GFM) features like tables and strikethrough.

Install these dependencies using npm or yarn:

npm install react-markdown remark-gfm
# or
yarn add react-markdown remark-gfm

Building the Markdown Editor Component

Let’s create the main component for our editor. Create a new file called `MarkdownEditor.js` in the `components` directory (you might need to create this directory). This component will handle the user input, Markdown rendering, and live preview.

Here’s the basic structure:

// components/MarkdownEditor.js
import React, { useState } from 'react';
import ReactMarkdown from 'react-markdown';
import { remark } from 'remark';
import gfm from 'remark-gfm';

function MarkdownEditor() {
  const [markdown, setMarkdown] = useState('');

  return (
    <div>
      <textarea
        rows="10"
        cols="50"
        value={markdown}
        onChange={(e) => setMarkdown(e.target.value)}
      />
      <div>
        <ReactMarkdown remarkPlugins={[gfm]}>{markdown}</ReactMarkdown>
      </div>
    </div>
  );
}

export default MarkdownEditor;

Let’s break down this code:

  • We import useState from React to manage the state of the Markdown text.
  • We import ReactMarkdown from react-markdown to render Markdown.
  • We import remark-gfm to support Github Flavored Markdown.
  • We initialize a state variable markdown with an empty string. This will hold the user’s input.
  • We create a textarea element where the user will enter their Markdown. The onChange event updates the markdown state with the user’s input.
  • We use the ReactMarkdown component to render the Markdown to HTML. The children prop takes the markdown state.
  • We use the remarkPlugins prop to enable GFM features.

Integrating the Editor into Your Page

Now, let’s integrate this component into your main page. Open `pages/index.js` and modify it as follows:

// pages/index.js
import MarkdownEditor from '../components/MarkdownEditor';

function Home() {
  return (
    <div style={{ padding: '20px' }}>
      <h2>Markdown Editor</h2>
      <MarkdownEditor />
    </div>
  );
}

export default Home;

Here, we import the MarkdownEditor component and render it within the Home component. The style attribute is added for some basic padding. Run your Next.js development server using npm run dev or yarn dev and navigate to http://localhost:3000 in your browser. You should see a text area and a preview area, and any text you type in the text area should appear in the preview area.

Adding Formatting Buttons

Let’s add some buttons to make it easier to format text. We’ll add buttons for bold, italic, and headings. Modify your MarkdownEditor.js file:

// components/MarkdownEditor.js
import React, { useState } from 'react';
import ReactMarkdown from 'react-markdown';
import { remark } from 'remark';
import gfm from 'remark-gfm';

function MarkdownEditor() {
  const [markdown, setMarkdown] = useState('');

  const handleBold = () => {
    setMarkdown(prevMarkdown => {
      const selectionStart = document.activeElement.selectionStart;
      const selectionEnd = document.activeElement.selectionEnd;
      const selectedText = document.activeElement.value.substring(selectionStart, selectionEnd);
      const newText = `**${selectedText}**`;
      return prevMarkdown.substring(0, selectionStart) + newText + prevMarkdown.substring(selectionEnd);
    });
  };

  const handleItalic = () => {
    setMarkdown(prevMarkdown => {
      const selectionStart = document.activeElement.selectionStart;
      const selectionEnd = document.activeElement.selectionEnd;
      const selectedText = document.activeElement.value.substring(selectionStart, selectionEnd);
      const newText = `*${selectedText}*`;
      return prevMarkdown.substring(0, selectionStart) + newText + prevMarkdown.substring(selectionEnd);
    });
  };

  const handleHeading = () => {
    setMarkdown(prevMarkdown => {
      const selectionStart = document.activeElement.selectionStart;
      const selectionEnd = document.activeElement.selectionEnd;
      const selectedText = document.activeElement.value.substring(selectionStart, selectionEnd);
      const newText = `# ${selectedText}`;
      return prevMarkdown.substring(0, selectionStart) + newText + prevMarkdown.substring(selectionEnd);
    });
  };

  return (
    <div>
      <div>
        <button onClick={handleBold}>Bold</button>
        <button onClick={handleItalic}>Italic</button>
        <button onClick={handleHeading}>Heading</button>
      </div>
      <textarea
        rows="10"
        cols="50"
        value={markdown}
        onChange={(e) => setMarkdown(e.target.value)}
      />
      <div>
        <ReactMarkdown remarkPlugins={[gfm]}>{markdown}</ReactMarkdown>
      </div>
    </div>
  );
}

export default MarkdownEditor;

We’ve added three button elements and their corresponding click handlers: handleBold, handleItalic, and handleHeading. These functions modify the markdown state by wrapping the selected text with the appropriate Markdown syntax. We use document.activeElement to get the currently focused element (the textarea), and then use selectionStart and selectionEnd to determine the selected text’s position. This approach allows users to select text and then apply formatting.

Adding More Formatting Options

You can extend the editor with more formatting options, such as:

  • Lists (ordered and unordered)
  • Links
  • Images
  • Code blocks
  • Tables

Implementing these features would involve adding more button handlers similar to the ones we’ve created. For example, to add a button for an unordered list, you’d add a function that inserts - at the beginning of each selected line.

Styling Your Editor

To improve the look and feel of your editor, you can add CSS styles. Here’s a basic example:

/* components/MarkdownEditor.css */
.markdown-editor {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.markdown-editor textarea {
  width: 100%;
  padding: 10px;
  font-family: monospace;
  border: 1px solid #ccc;
  border-radius: 4px;
}

.markdown-editor .preview {
  border: 1px solid #eee;
  padding: 10px;
  border-radius: 4px;
}

.markdown-editor button {
  padding: 5px 10px;
  border: 1px solid #ddd;
  border-radius: 4px;
  background-color: #f9f9f9;
  cursor: pointer;
}

Apply these styles by importing the CSS file into your MarkdownEditor.js component and adding the class names to your HTML elements. For example:

// components/MarkdownEditor.js
import React, { useState } from 'react';
import ReactMarkdown from 'react-markdown';
import { remark } from 'remark';
import gfm from 'remark-gfm';
import './MarkdownEditor.css'; // Import the CSS file

function MarkdownEditor() {
  const [markdown, setMarkdown] = useState('');

  // ... (button handlers)

  return (
    <div className="markdown-editor">
      <div>
        <button onClick={handleBold}>Bold</button>
        <button onClick={handleItalic}>Italic</button>
        <button onClick={handleHeading}>Heading</button>
      </div>
      <textarea
        rows="10"
        cols="50"
        value={markdown}
        onChange={(e) => setMarkdown(e.target.value)}
      />
      <div className="preview">
        <ReactMarkdown remarkPlugins={[gfm]}>{markdown}</ReactMarkdown>
      </div>
    </div>
  );
}

export default MarkdownEditor;

Common Mistakes and How to Fix Them

Here are some common mistakes and how to avoid them:

  • Incorrect Dependency Installation: Make sure you install the correct dependencies. Check the spelling of the packages (react-markdown and remark-gfm) in your package.json file.
  • Missing Import Statements: Ensure you import all the necessary components and modules correctly. Double-check your import statements, especially for useState and ReactMarkdown.
  • Incorrect State Updates: When updating the state, ensure you’re using the correct syntax. For example, using the functional update form of setMarkdown when you need the previous state.
  • Markdown Rendering Issues: If your Markdown isn’t rendering correctly, double-check your Markdown syntax, and make sure that you have correctly imported and configured the react-markdown and remark-gfm plugins.
  • CSS Styling Issues: If your styles aren’t applied, ensure you’ve imported the CSS file correctly and that your class names match the ones in your CSS file.

Key Takeaways

  • Next.js is a powerful framework for building interactive web applications.
  • react-markdown and remark-gfm are excellent libraries for rendering Markdown in React.
  • State management is crucial for handling user input and updating the UI.
  • Adding formatting buttons enhances the user experience.
  • CSS styling improves the visual appearance of your editor.

FAQ

  1. Can I use this editor for a blog? Yes, you can adapt this editor for a blog by integrating it with a backend to save and retrieve the Markdown content. You can also add features such as image uploading and more advanced formatting options.
  2. How can I add more formatting options? You can add more formatting options by creating additional button handlers. Each handler should modify the markdown state by wrapping the selected text with the appropriate Markdown syntax.
  3. How do I handle images? Handling images involves integrating an image upload feature. You would typically use a library or service to upload the images to a server and then insert the Markdown syntax for images (![alt text](image url)) into the text area.
  4. Can I save the content? Yes, you can add a save feature by implementing a mechanism to store the markdown content. This could involve using local storage, a database, or a backend API to save the content.
  5. How can I deploy this editor? You can deploy your Next.js application to various platforms, such as Vercel, Netlify, or AWS. These platforms provide simple deployment processes and handle the server-side rendering and build processes for you.

By following this tutorial, you’ve built a functional Markdown editor. This project provides a solid foundation for understanding Next.js, Markdown rendering, and state management in React. You can now extend this editor with additional features and custom styling to create a tailored content creation experience.