Build a Simple Node.js Interactive Web-Based Markdown Previewer

Written by

in

In the world of web development, the ability to preview Markdown in real-time is a valuable skill. Whether you’re writing documentation, creating blog posts, or simply taking notes, a Markdown previewer allows you to see how your text will look when rendered as HTML. This tutorial will guide you through building a simple, interactive web-based Markdown previewer using Node.js, Express, and a Markdown parser. This project is perfect for beginners and intermediate developers looking to expand their knowledge of Node.js and front-end interaction.

Why Build a Markdown Previewer?

Markdown is a lightweight markup language with plain text formatting syntax. It’s widely used for its simplicity and readability. A Markdown previewer enhances the writing experience by providing instant feedback on how your Markdown text will be displayed. This eliminates the need to switch between an editor and a separate preview window, saving time and improving workflow. Furthermore, understanding how to build such a tool is a great way to learn about web server setup, front-end interaction (using JavaScript), and the use of external libraries within a Node.js environment.

What We’ll Cover

In this tutorial, we will:

  • Set up a Node.js and Express server.
  • Install and use a Markdown parser (marked).
  • Create an HTML interface with a text area for Markdown input and a display area for the rendered HTML.
  • Implement real-time preview functionality using JavaScript.
  • Handle user input and update the preview dynamically.

Prerequisites

Before we begin, make sure you have the following installed:

  • Node.js and npm (Node Package Manager)
  • A code editor (e.g., VS Code, Sublime Text, Atom)

Setting Up the Project

Let’s start by creating a new project directory and initializing our Node.js project. Open your terminal or command prompt and run the following commands:

mkdir markdown-previewer
cd markdown-previewer
npm init -y

This will create a new directory named “markdown-previewer”, navigate into it, and initialize a new Node.js project with a default `package.json` file.

Installing Dependencies

Next, we need to install the necessary dependencies. We’ll be using Express.js for our web server and marked for parsing Markdown.

npm install express marked --save

This command installs Express and marked and saves them as project dependencies. `express` will handle our web server, and `marked` will convert Markdown to HTML.

Creating the Server (server.js)

Create a file named `server.js` in your project directory. This file will contain the code for our Express server.

const express = require('express');
const marked = require('marked');
const path = require('path');

const app = express();
const port = process.env.PORT || 3000;

// Middleware to serve static files (e.g., CSS, JavaScript)
app.use(express.static('public'));

// Middleware to parse URL-encoded bodies
app.use(express.urlencoded({ extended: true }));

// Set the view engine to EJS (or any other you prefer)
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));

// Route for the main page
app.get('/', (req, res) => {
  res.render('index', { markdownContent: '' }); // Pass an empty string to the template
});

// Route to handle Markdown conversion
app.post('/preview', (req, res) => {
  const markdown = req.body.markdown;
  const html = marked.parse(markdown);
  res.json({ html });
});

app.listen(port, () => {
  console.log(`Server listening on port ${port}`);
});

In this code:

  • We import the `express` and `marked` modules.
  • We create an Express application.
  • We define the port (using environment variables or defaulting to 3000).
  • We use `express.static` to serve static files from a “public” directory (we’ll create this later).
  • We use `express.urlencoded` to parse URL-encoded data from POST requests.
  • We set the view engine to EJS.
  • We define a route (`/`) to render the `index.ejs` view (we’ll create this view file shortly) and pass an empty string initially.
  • We define a route (`/preview`) to handle POST requests. This route receives the Markdown from the client, converts it to HTML using `marked.parse()`, and sends the HTML back as JSON.
  • We start the server and listen on the specified port.

Creating the HTML Interface (index.ejs)

Create a directory named “views” in your project directory, and inside it, create a file named `index.ejs`. This file will contain the HTML structure for our Markdown previewer.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Markdown Previewer</title>
    <link rel="stylesheet" href="/style.css">
</head>
<body>
    <div class="container">
        <div class="input-area">
            <textarea id="markdown-input" placeholder="Enter Markdown here..."><%= markdownContent %></textarea>
        </div>
        <div class="preview-area">
            <div id="preview"></div>
        </div>
    </div>
    <script src="/script.js"></script>
</body>
</html>

In this EJS file:

  • We define the basic HTML structure, including a title and a link to a CSS file (`style.css`), which we’ll create later.
  • We have a `textarea` element with the ID “markdown-input” where the user will enter their Markdown. The `<%= markdownContent %>` is an EJS expression that will render the initial markdown content passed from the server.
  • We have a `div` element with the ID “preview” where the rendered HTML will be displayed.
  • We include a script file `script.js` (we’ll create this in the next step) to handle the front-end logic.

Creating the CSS (style.css)

Create a directory named “public” in your project directory. Inside the “public” directory, create a file named `style.css`. This file will contain the CSS styles for our Markdown previewer.

body {
    font-family: sans-serif;
    margin: 0;
    padding: 0;
    background-color: #f4f4f4;
    color: #333;
}

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

.input-area, .preview-area {
    width: 80%;
    margin-bottom: 20px;
}

textarea {
    width: 100%;
    height: 300px;
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
    font-family: monospace;
    font-size: 14px;
    resize: vertical;
}

#preview {
    width: 100%;
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
    background-color: #fff;
    overflow-x: auto;
}

/* Optional: Basic styling for Markdown elements */
h1, h2, h3, h4, h5, h6 {
    margin-top: 1em;
    margin-bottom: 0.5em;
}

p {
    margin-bottom: 1em;
}

code {
    background-color: #eee;
    padding: 2px 4px;
    border-radius: 4px;
}

This CSS provides basic styling for the layout and elements of our previewer.

Creating the JavaScript (script.js)

Inside the “public” directory, create a file named `script.js`. This file will contain the JavaScript code to handle the real-time preview functionality.

const markdownInput = document.getElementById('markdown-input');
const preview = document.getElementById('preview');

// Function to update the preview
function updatePreview() {
  const markdownText = markdownInput.value;

  fetch('/preview', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: `markdown=${encodeURIComponent(markdownText)}`,
  })
  .then(response => response.json())
  .then(data => {
    preview.innerHTML = data.html;
  })
  .catch(error => {
    console.error('Error:', error);
    preview.innerHTML = '<p>Error rendering Markdown.</p>'; // Display error message
  });
}

// Initial preview on page load
updatePreview();

// Event listener for input changes
markdownInput.addEventListener('input', updatePreview);

Let’s break down this JavaScript code:

  • We get references to the `textarea` (markdownInput) and the `div` (preview) elements using their IDs.
  • The `updatePreview()` function is defined. This function does the following:
    • Gets the Markdown text from the `textarea`.
    • Uses the `fetch` API to send a POST request to the `/preview` endpoint (defined in `server.js`).
    • The POST request includes the markdown text in the request body.
    • The server endpoint processes the Markdown to HTML and returns it in JSON.
    • The response is parsed as JSON, and the HTML is then inserted into the `preview` div.
    • Error handling is included to display an error message if the Markdown rendering fails.
  • We call `updatePreview()` initially to render any pre-existing markdown (e.g. from the EJS initial render).
  • We add an event listener to the `textarea` to call `updatePreview()` whenever the user types something into the text area. This ensures the preview updates in real-time.

Running the Application

Now that we have all the files set up, let’s run the application. In your terminal, make sure you are in the project directory, and run the following command:

node server.js

This will start the Express server. Open your web browser and go to `http://localhost:3000`. You should see the Markdown previewer. Type Markdown in the left text area, and the rendered HTML will appear in the right preview area.

Common Mistakes and Troubleshooting

Here are some common mistakes and how to fix them:

  • Incorrect File Paths: Double-check that all file paths in your `server.js`, `index.ejs`, and `script.js` are correct. For example, ensure the CSS and JavaScript files are located where your HTML expects them to be.
  • Server Not Running: Make sure the Node.js server is running in your terminal. If you make changes to `server.js`, you’ll need to restart the server for the changes to take effect.
  • Dependencies Not Installed: Ensure that you have installed the required dependencies (Express and marked) by running `npm install` in your project directory.
  • CORS Errors: If you encounter CORS (Cross-Origin Resource Sharing) errors, it’s likely a problem if you’re trying to fetch data from a different domain. For this simple app, CORS should not be an issue as the front-end and back-end are running on the same server.
  • Markdown Rendering Issues: If the Markdown isn’t rendering correctly, double-check your Markdown syntax. Also, ensure the `marked` library is working correctly. You can try simpler Markdown examples (e.g., `# Heading`, `**bold text**`) to test.
  • JavaScript Errors: Use your browser’s developer console (usually accessed by pressing F12) to check for JavaScript errors. These errors can often provide clues about what’s going wrong.
  • EJS Syntax Errors: If your EJS templates aren’t rendering correctly, check for syntax errors in your `index.ejs` file.

Key Takeaways

  • You’ve learned how to set up a basic Node.js and Express server.
  • You’ve learned how to use the `marked` library to parse Markdown.
  • You’ve created a simple HTML interface with a text area and a preview area.
  • You’ve implemented real-time preview functionality using JavaScript.
  • You’ve learned how to handle user input and update the preview dynamically.

FAQ

Q: Can I use a different Markdown parser?

A: Yes, you can. There are several Markdown parsers available for Node.js. Simply install the parser of your choice (e.g., `markdown-it`) and update the `marked.parse()` call in your `server.js` file with the appropriate function from the new parser.

Q: How can I add more styling to the preview?

A: You can add CSS styles to the `style.css` file to customize the appearance of the rendered HTML. You can also use CSS frameworks like Bootstrap or Tailwind CSS to quickly style your previewer.

Q: How can I add syntax highlighting to the code blocks in the preview?

A: You can use a library like Prism.js or highlight.js. Install the library, include its CSS and JavaScript files in your HTML, and then use the library’s functions to highlight the code blocks in your preview.

Q: How can I deploy this application?

A: You can deploy this application to platforms like Heroku, Netlify, or AWS. You’ll need to create an account on the chosen platform, push your code to a repository (e.g., GitHub), and configure the platform to build and deploy your application.

Q: Can I save the Markdown content to a file or database?

A: Yes, you can. You would need to add functionality to handle saving and retrieving the Markdown content. This typically involves using Node.js modules like `fs` (for file system operations) or a database library (e.g., `mongoose` for MongoDB, or `pg` for PostgreSQL) to interact with a database.

Building a Markdown previewer offers a practical introduction to full-stack web development. You’ve tackled server-side setup, front-end interaction, and the use of external libraries. This project is a solid foundation for further exploration in web technologies. Consider expanding this project with features like saving and loading Markdown files, adding syntax highlighting, or integrating a more advanced text editor. The skills you’ve gained here are transferable to a wide range of web development tasks, and with each new feature you add, your understanding and expertise will only grow.