Build a Next.js Interactive Web-Based Markdown Previewer

Written by

in

In the world of web development, the ability to instantly see the results of your code is invaluable. This is especially true when working with Markdown, a lightweight markup language widely used for formatting text on the web. Imagine writing a blog post, a README file, or even just taking notes, and being able to see the rendered HTML in real-time. This is where a Markdown previewer comes in handy. This tutorial will guide you through building an interactive Markdown previewer using Next.js, a powerful React framework for production.

Why Build a Markdown Previewer?

A Markdown previewer offers several benefits:

  • Real-time Feedback: See your Markdown rendered as HTML instantly, allowing for quick adjustments and validation.
  • Improved Workflow: Streamlines the writing process by eliminating the need to repeatedly save, preview, and edit.
  • Learning Tool: Helps you understand how Markdown syntax translates to HTML.
  • Accessibility: Ensures your Markdown is correctly formatted for accessibility, as you can immediately see the rendered output.

By building your own previewer, you’ll gain a deeper understanding of Next.js, React, and Markdown parsing, all while creating a practical tool.

Prerequisites

Before we begin, make sure you have the following:

  • Node.js and npm (or yarn) installed: These are essential for managing project dependencies and running the development server.
  • Basic knowledge of JavaScript and React: Familiarity with these technologies will help you understand the code.
  • A code editor: VS Code, Sublime Text, or any other editor of your choice.

Setting Up Your Next.js Project

Let’s start by creating a new Next.js project. Open your terminal and run the following command:

npx create-next-app markdown-previewer

This command will create a new directory named “markdown-previewer” with the basic structure of a Next.js application. Navigate into the project directory:

cd markdown-previewer

Now, start the development server:

npm run dev

Open your browser and go to http://localhost:3000. You should see the default Next.js welcome page.

Installing Dependencies

We’ll need a library to parse Markdown into HTML. A popular choice is “marked”. Install it using npm or yarn:

npm install marked

Or with yarn:

yarn add marked

Building the Markdown Previewer Component

Let’s create the core component for our previewer. We’ll modify the `pages/index.js` file, which is the default page in our Next.js application. Replace the content of `pages/index.js` with the following code:

import { useState } from 'react';
import { marked } from 'marked';

export default function Home() {
  const [markdown, setMarkdown] = useState('');

  const handleInputChange = (event) => {
    setMarkdown(event.target.value);
  };

  return (
    <div className="container">
      <h1>Markdown Previewer</h1>
      <div className="input-container">
        <textarea
          className="input"
          value={markdown}
          onChange={handleInputChange}
          placeholder="Enter Markdown here..."
        />
      </div>
      <div className="preview-container">
        <h2>Preview</h2>
        <div
          className="preview"
          dangerouslySetInnerHTML={{ __html: marked(markdown) }}
        />
      </div>
    </div>
  );
}

Let’s break down this code:

  • Import Statements: We import `useState` from React for managing the state of our Markdown input, and `marked` from the `marked` library to convert Markdown to HTML.
  • State: We use `useState` to create a state variable called `markdown`, initialized as an empty string. This variable will hold the Markdown text entered by the user.
  • `handleInputChange` Function: This function is called whenever the text in the textarea changes. It updates the `markdown` state with the new value.
  • JSX Structure:
    • A main `div` with the class “container” to hold the entire application.
    • An `h1` heading for the title.
    • An input container with a `textarea` for the user to enter Markdown. The `value` is bound to the `markdown` state, and the `onChange` event calls `handleInputChange`.
    • A preview container with an `h2` heading and a `div` to display the rendered HTML.
    • The `dangerouslySetInnerHTML` prop is used to inject the HTML generated by `marked(markdown)` into the `div`. This is necessary because `marked` returns HTML strings.

Styling the Component

To make our previewer look good, let’s add some basic CSS. Create a file named `styles/Home.module.css` in your project’s `styles` directory and add the following CSS:

.container {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px;
  font-family: sans-serif;
}

.input-container {
  width: 80%;
  margin-bottom: 20px;
}

.input {
  width: 100%;
  height: 200px;
  padding: 10px;
  font-size: 16px;
  border: 1px solid #ccc;
  border-radius: 4px;
  resize: vertical;
}

.preview-container {
  width: 80%;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 10px;
}

.preview {
  padding: 10px;
  border: 1px solid #eee;
  border-radius: 4px;
  word-wrap: break-word;
  /* Add more styles to the preview as needed */
}

Now, import this CSS file into your `pages/index.js` file. Add the following line at the top of the file, below the import statements for `useState` and `marked`:

import styles from '../styles/Home.module.css';

And then, in your `pages/index.js` file, modify the main `div` to include the `container` class from the `styles` object:

<div className={styles.container}>

You may also want to add the other styles from the `styles` object in the relevant places in your JSX. For example:

<div className={styles.input-container}>
  <textarea className={styles.input} ... ></textarea>
</div>
<div className={styles.preview-container}>
  <div className={styles.preview} dangerouslySetInnerHTML={{ __html: marked(markdown) }}></div>
</div>

Refresh your browser. You should now see the Markdown previewer with a text area and a preview area. As you type Markdown in the text area, the rendered HTML will appear in the preview area.

Testing the Previewer

Let’s test our Markdown previewer. Try entering some Markdown syntax into the text area. Here are a few examples:

  • Headings: `
    # This is a heading
    ## This is a subheading
    `
  • Bold and Italics: `
    This is **bold text** and *italic text*.
    `
  • Lists: `
    * Item 1
    * Item 2
    `
  • Links: `
    [Google](https://www.google.com)
    `
  • Code Blocks: `
    “`javascript
    console.log(‘Hello, world!’);
    “`
    `

The preview area should instantly render the corresponding HTML for each of these examples. If it doesn’t, double-check your code for any errors and make sure you’ve installed the `marked` library correctly.

Common Mistakes and Troubleshooting

Here are some common mistakes and how to fix them:

  • Missing or Incorrect Import: Make sure you have correctly imported `marked` and `useState`.
  • Typographical Errors: Double-check your code for any typos, especially in the component names, variable names, and class names.
  • Incorrect CSS Styling: If the styling doesn’t appear as expected, check the CSS file and ensure that the class names match the ones in your JSX. Also, make sure that you’ve imported the CSS file correctly in your JavaScript file.
  • `dangerouslySetInnerHTML` Usage: Remember that `dangerouslySetInnerHTML` can potentially introduce security vulnerabilities if the content is not properly sanitized. In this case, we are using the `marked` library, which sanitizes the input, but be cautious when using this prop with user-provided content.
  • Markdown Parsing Errors: If your Markdown isn’t rendering correctly, ensure that the Markdown syntax is valid. Also, some Markdown parsers have different feature sets. The `marked` library is very comprehensive, but some less common syntax might not be supported.
  • CORS Issues: If you are fetching external resources (images, etc.) from different domains, you might encounter CORS (Cross-Origin Resource Sharing) issues. Make sure the server hosting those resources allows requests from your domain.

Enhancements and Next Steps

Here are some ways to enhance your Markdown previewer:

  • Add a Toolbar: Implement a toolbar with buttons to easily format text (bold, italic, headings, etc.).
  • Syntax Highlighting: Integrate a code syntax highlighter (like Prism.js or highlight.js) to make code blocks more readable.
  • Image Upload: Allow users to upload images and display them in the preview.
  • Live Preview Updates: Implement a debouncing or throttling mechanism to prevent the preview from updating too frequently as the user types, which can improve performance.
  • Custom Styles: Allow users to customize the preview’s appearance with their own CSS styles.
  • Error Handling: Implement error handling to gracefully handle invalid Markdown or other unexpected issues.
  • Save/Load Functionality: Add features to save and load Markdown from local storage or a backend.

Key Takeaways

In this tutorial, you’ve learned how to build an interactive Markdown previewer using Next.js. You’ve seen how to:

  • Set up a Next.js project.
  • Install and use the `marked` library to parse Markdown.
  • Create a React component with a text area and a preview area.
  • Use `useState` to manage the state of the Markdown input.
  • Use `dangerouslySetInnerHTML` to render the HTML generated by `marked`.
  • Add basic CSS styling.

FAQ

  1. Can I use a different Markdown parser?

    Yes, you can use any Markdown parser library that works in JavaScript. `marked` is a popular choice, but other options include `markdown-it`, `remarkable`, and others. The core logic of the previewer will remain the same; you just need to adjust the import statement and the function used to parse the Markdown.

  2. How can I deploy this application?

    You can deploy your Next.js application to various platforms, such as Vercel (which is recommended for Next.js), Netlify, or other hosting providers that support Node.js applications. You’ll typically just need to push your code to a Git repository and configure the deployment platform to build and deploy your application.

  3. How do I handle user input sanitization?

    The `marked` library, by default, sanitizes user input, meaning it removes potentially harmful HTML tags and attributes from the rendered output. However, if you are integrating other features that allow users to provide HTML or other content, it’s essential to use a proper sanitization library to prevent cross-site scripting (XSS) vulnerabilities. Consider using libraries like `dompurify` for more advanced sanitization needs.

  4. How can I add syntax highlighting to the code blocks?

    You can integrate a syntax highlighting library like Prism.js or highlight.js. You’ll need to install the library, import its CSS and JavaScript files, and then use the library’s functions to highlight the code blocks in the rendered HTML. Typically, you’ll need to wrap the code blocks in `

    ` and `` tags and apply the appropriate class names for the syntax highlighting.

Building this Markdown previewer is a great starting point for any web developer. It demonstrates how to combine front-end technologies with a useful practical application. This project allows you to practice React state management, component composition, and working with external libraries. As your skills grow, you can expand this project to include more advanced features and customizations. Embrace the opportunity to experiment and learn, and you'll find yourself creating increasingly complex and powerful web applications. This is only the beginning of what you can accomplish with Next.js and the power of interactive web tools.