Build a Node.js Interactive Web-Based Markdown Previewer

Written by

in

In the digital age, the ability to quickly format and preview text is a crucial skill for writers, developers, and anyone who creates content. Markdown, a lightweight markup language, simplifies text formatting, making it easier to write and read. However, writing in Markdown can be a bit cumbersome without a real-time preview. This is where a web-based Markdown previewer comes in handy. In this tutorial, we will build a simple, interactive Markdown previewer using Node.js, providing a live update of your formatted text as you type. This project will not only introduce you to the basics of Node.js and front-end interaction but also equip you with a practical tool for your daily workflow.

Why Build a Markdown Previewer?

Imagine you’re drafting a blog post, writing documentation, or even just taking notes. Using Markdown allows you to focus on the content without getting bogged down in complex formatting. But, how do you know what your text will look like when it’s published? A Markdown previewer solves this problem by rendering your Markdown in real-time. This immediate feedback helps you to visualize the final output, ensuring your content is well-formatted and easy to read. Moreover, building this project will provide hands-on experience with important web development concepts.

Prerequisites

Before we dive in, let’s make sure you have everything you need:

  • Node.js and npm: Make sure you have Node.js and npm (Node Package Manager) installed on your system. You can download them from the official Node.js website.
  • A Code Editor: You’ll need a code editor, such as VS Code, Sublime Text, or Atom.
  • Basic HTML, CSS, and JavaScript Knowledge: While this tutorial will guide you, a basic understanding of these technologies will be helpful.

Project Setup

Let’s get started by setting up our project. Open your terminal or command prompt and follow these steps:

  1. Create a Project Directory: Create a new directory for your project and navigate into it.
  2. Initialize npm: Initialize a new npm project using the command npm init -y. This will create a package.json file in your project directory.
  3. Install Dependencies: We’ll need a few dependencies for this project:
    • express: A web application framework for Node.js.
    • marked: A Markdown parser.
    • body-parser: Middleware to parse request bodies.

    Install these dependencies using the command: npm install express marked body-parser

Your project directory should now look something like this:

markdown-previewer/
├── node_modules/
├── package.json
└── package-lock.json

Building the Server-Side (Node.js)

Now, let’s create the server-side logic using Node.js and Express. Create a new file named server.js in your project directory. This file will handle incoming requests, process Markdown, and serve the HTML page.

Here’s the code for server.js:

const express = require('express');
const bodyParser = require('body-parser');
const marked = require('marked');
const path = require('path');

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

// Middleware
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('public')); // Serve static files (CSS, JS)

// Set the view engine to EJS (you can use other templating engines too)
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));

// Routes
app.get('/', (req, res) => {
  res.render('index', { markdown: '' }); // Render the index.ejs template
});

app.post('/preview', (req, res) => {
  const markdownText = req.body.markdown;
  const html = marked.parse(markdownText);
  res.json({ html: html }); // Send the rendered HTML as JSON
});

// Start the server
app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

Let’s break down this code:

  • Importing Modules: We import the necessary modules: express for creating the server, body-parser to parse the request body, marked to parse Markdown, and path to handle file paths.
  • Initializing Express App: We create an Express application instance and set the port.
  • Middleware: We use bodyParser.urlencoded({ extended: true }) to parse URL-encoded data from the request body. We also serve static files (CSS, JavaScript) from a ‘public’ directory.
  • Setting up the View Engine: We set up EJS as our view engine and specify the ‘views’ directory where our template files will be stored.
  • Routes:
    • GET /: This route renders the index.ejs template and passes an empty string as the initial Markdown content.
    • POST /preview: This route handles the preview functionality. It receives Markdown text from the client, parses it using marked.parse(), and sends the rendered HTML back to the client as JSON.
  • Starting the Server: Finally, we start the server and listen on the specified port.

Creating the Front-End (HTML, CSS, JavaScript)

Next, we’ll create the front-end components, including the HTML structure, CSS styling, and JavaScript logic to handle user input and display the preview.

HTML (index.ejs)

Create a directory named views in your project directory. Inside the views directory, create a file named index.ejs. This file will contain the HTML structure of our application.

<!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="/styles.css">
</head>
<body>
  <div class="container">
    <div class="input-container">
      <textarea id="editor" placeholder="Enter Markdown here"><%= markdown %></textarea>
    </div>
    <div class="preview-container">
      <div id="preview"></div>
    </div>
  </div>
  <script src="/script.js"></script>
</body>
</html>

Here’s what this HTML does:

  • Basic Structure: It defines the basic HTML structure with a title, a viewport meta tag, and links to CSS and JavaScript files.
  • Input Area: It includes a textarea element with the id editor, where the user will enter their Markdown. The initial value is set to the markdown variable passed from the server.
  • Preview Area: It includes a div element with the id preview, where the rendered HTML will be displayed.
  • Linking CSS and JavaScript: It links to a styles.css file for styling and a script.js file for JavaScript functionality.

CSS (public/styles.css)

Create a directory named public in your project directory. Inside the public directory, create a file named styles.css. This file will contain the CSS styles for our application.

body {
  font-family: sans-serif;
  margin: 0;
  padding: 0;
  background-color: #f4f4f4;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
}

.container {
  display: flex;
  width: 80%;
  max-width: 1200px;
  margin: 20px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  border-radius: 8px;
  overflow: hidden;
}

.input-container, .preview-container {
  width: 50%;
  padding: 20px;
  box-sizing: border-box;
}

textarea {
  width: 100%;
  height: 80vh;
  padding: 10px;
  font-size: 16px;
  border: 1px solid #ccc;
  border-radius: 4px;
  resize: none;
}

#preview {
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
  background-color: #fff;
  overflow-y: scroll;
  height: 80vh;
  font-size: 16px;
}

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

p {
  margin-bottom: 1em;
}

ul, ol {
  margin-bottom: 1em;
  padding-left: 20px;
}

code {
  font-family: monospace;
  background-color: #f0f0f0;
  padding: 2px 4px;
  border-radius: 4px;
}

pre {
  background-color: #f0f0f0;
  padding: 10px;
  border-radius: 4px;
  overflow-x: auto;
}

code {
  font-family: monospace;
  background-color: #f0f0f0;
  padding: 2px 4px;
  border-radius: 4px;
}

This CSS provides basic styling for the HTML elements, including the layout of the input and preview areas, the appearance of the text area, and some basic Markdown element styling (headings, paragraphs, lists, code blocks).

JavaScript (public/script.js)

Inside the public directory, create a file named script.js. This file will contain the JavaScript code to handle user input and update the preview.

const editor = document.getElementById('editor');
const preview = document.getElementById('preview');

// Function to update the preview
function updatePreview() {
  const markdownText = editor.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;
  });
}

// Add an event listener to the textarea for input changes
editor.addEventListener('input', updatePreview);

// Initial preview update
updatePreview();

Let’s break down this JavaScript code:

  • Get Elements: It selects the textarea element with the id editor and the div element with the id preview.
  • updatePreview Function:
    • Gets the Markdown text from the editor.
    • Uses the fetch API to send a POST request to the /preview route on the server, sending the Markdown text in the request body.
    • Parses the response as JSON.
    • Updates the innerHTML of the preview div with the rendered HTML received from the server.
  • Event Listener: It adds an input event listener to the editor textarea. This means that whenever the user types something in the textarea, the updatePreview function is called.
  • Initial Update: It calls the updatePreview function once when the page loads to display any initial Markdown content.

Running the Application

Now that we have all the components set up, let’s run the application. Open your terminal in your project directory and run the following command:

node server.js

This will start the Node.js 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 formatted output should appear in the right preview area in real-time.

Common Mistakes and Troubleshooting

Here are some common mistakes and how to fix them:

  • Server Not Running: Make sure your Node.js server is running. If you see a blank page, check the console for any errors.
  • Incorrect File Paths: Double-check your file paths in the HTML, CSS, and JavaScript files. Make sure they point to the correct locations.
  • CORS Issues: If you encounter CORS (Cross-Origin Resource Sharing) issues, make sure your server is configured to handle requests from your client (browser). This is usually not an issue in this simple setup.
  • Markdown Parsing Errors: If the Markdown isn’t rendering correctly, ensure the marked library is correctly installed and that the syntax is valid.
  • CSS Not Applying: If the CSS isn’t applying, check the link in your HTML to the styles.css file and the path to the file in the public folder.
  • JavaScript Errors: Use your browser’s developer console (usually accessed by pressing F12) to check for JavaScript errors. These can often provide clues about what’s going wrong.

SEO Best Practices

To make your blog post rank well on search engines, consider these SEO best practices:

  • Keyword Optimization: Naturally include relevant keywords like “Node.js”, “Markdown”, “previewer”, and “web development” throughout your content, title, and meta description.
  • Meta Description: Write a concise and compelling meta description (within 160 characters) that summarizes the article and includes relevant keywords.
  • Heading Tags: Use heading tags (<h2>, <h3>, <h4>) to structure your content and make it easier to read.
  • Image Alt Text: While this tutorial doesn’t include images, remember to add descriptive alt text to images in other articles.
  • Internal Linking: Link to other relevant articles on your blog.
  • Mobile-Friendly Design: Ensure your application and blog post are responsive and display well on all devices.
  • Content Quality: Provide high-quality, original content that is helpful and informative.

Key Takeaways

  • You’ve successfully built a functional Markdown previewer using Node.js, Express, and the marked library.
  • You’ve learned how to set up a basic Node.js server, handle routes, and serve static files.
  • You’ve gained experience with front-end technologies like HTML, CSS, and JavaScript.
  • You’ve learned how to make an interactive web application that dynamically updates content.

FAQ

  1. Can I use a different templating engine?

    Yes, you can. While this tutorial uses EJS, you can use other templating engines like Pug (formerly Jade) or Handlebars. You’ll need to install the appropriate npm package and update the server and view files accordingly.

  2. How can I add more features to the previewer?

    You can add features like syntax highlighting for code blocks, a toolbar for common Markdown formatting options, and the ability to save and load Markdown files. You can also explore different Markdown parsers and themes for styling.

  3. What if I want to deploy this application?

    You can deploy this application to platforms like Heroku, Netlify, or AWS. You’ll need to create a Procfile (for Heroku) or configure the platform to run your server.js file. You may also need to configure environment variables for the port.

  4. How do I handle different Markdown flavors?

    The marked library supports various Markdown features. If you need specific Markdown extensions, you may need to configure marked with options or explore other Markdown parsers that support more advanced features.

This project serves as a solid foundation for understanding the fundamentals of web development with Node.js. With this foundation, you can expand your skills, build more complex applications, and explore the vast world of web development. From here, you can customize the appearance, add features, and integrate it into your own workflows. The ability to create tools that enhance your productivity is a valuable skill in today’s world, and this project is a great starting point for that journey.