Thursday, July 4, 2024
HomeLanguagesReactDesigning a Markdown note taking application in ReactJS

Designing a Markdown note taking application in ReactJS

In this article, we will learn how to design a Markdown note-taking application in React that can help one to quickly jot down notes and download the rendered output. The use of Markdown allows one to spend less time on formatting the text and therefore create rich text content using only the Markdown syntax.

We will be using the React library to quickly create the User Interface as it would handle the updating of the DOM as required. We will use Bootstrap CSS to design the application and make it user-friendly. We will also use a Blob to save the content to our device.

We will be following the below steps for creating our application:

Step 1: Creating the project

We will start by creating a new react project using the create-react-app tool. You can use the following command for creating one with the desired name. Please ensure that Node and npm are installed on the system.

npx create-react-app react-md-note-app

Project Structure: We will then delete all the files that are not necessary. The file structure after deleting the files is given below:

Project Directory

Step 2: Installing necessary modules

We will start by adding the Bootstrap CDN to the index.html page. The CDN links can be found here and they can directly be added in the head section of the index page.

We will also need two npm packages, one is the markdown-it package that does the heavy-lifting of parsing the Markdown content and giving us the rendered output in HTML. We can simply use this HTML for displaying our output. The other module is the file-saver package that simplifies the task of saving the given blob to the system. The following command is used for installing these two packages:

npm install markdown-it file-saver

Step 3: Creating a simple User Interface using Bootstrap

We will create the layout using the Bootstrap Grid system to ensure that our application is mobile responsive and looks good without writing a lot of CSS.

The application can be divided into 3 parts; the markdown editor that will allow the user to enter the notes in Markdown, the output area that shows the rendered HTML, and the file saving options that contain the input field for the filename and the buttons for saving the content. Each of the parts is nested using the Card component of Bootstrap and the relevant components are added to the input elements.

We will also need some CSS rules that can be imported into our App.js file. We can create a new file named App.css and write the rules as below:

App.css:

CSS




body {
  background-color: yellow; 
.markdown-editor, .rendered-output {
  height: 100%;
  width: 100%;
  padding: 10px
.markdown-textarea, .rendered-html-output {
  height: 60vh;
  width: 100%;
  overflow: auto;
}


Step 4: Creating the hooks that will be used to manage the state

We will create 3 hooks using the useState method of React for managing the state in the 3 parts of our application. Each of the hooks returns the variable itself with its initial state and the function that can be used to set the state of that variable. React will automatically re-render the component when its state changes.

This will allow us to simply use the respective functions and the variables for handling the state of each component.

Step 5: Using the state variables in the application

We will now use these variables inside the 3 components we created earlier.

The markdownText variable is used in the textarea where we will write our markdown text. We will use this in the value property of the textarea. Using the setMarkdownText function we can change the value of the text here and it would be displayed via this value property.

The renderedHTML variable is used in the div element that will show the rendered output of the markdown. As the MarkdownIt module will return the HTML from the markdown, we will need to set the inner HTML of this element directly. This will require the use of the dangerouslySetInnerHTML property of the element and passing an object with the key of _html.

Note: This is a feature in React that discourages the practice of directly modifying the HTML to prevent Cross-Site Scripting (XSS) attacks on a webpage. It is recommended to sanitize any user-input HTML content before directly using it.

Finally, the fileName variable is used in the file saver options so that it can be used to enter the filename of the content to be saved. We will also bind the onChange event handler to setFileName so that any value entered by the user is updated to the variable. We can do this by accessing the event target and getting the value property.

Step 6: Creating the functions we have defined earlier

The handleTextInput() function: This function will take the text that is entered in the markdown editor and use MarkdownIt to render the HTML needed.

We will first get the text that is entered by getting the value of the target. We then initialize a new instance of MarkdownIt and use its render method. We will then use setMarkdownHTML function to set this in the output area where our final HTML will be displayed. This function will be used in the onChange property of the markdown editor.

The saveHTML() and saveMarkdown() functions: These functions will be used to save content to our system with the given filename. In both of these functions, we will create a Blob object with the content to be saved. We can use the renderedHTML and markdownText variables to get the current text content of the application.

The type of the blob is specified next. We can use text/html for the rendered output so that it is saved as an HTML file and only text for the markdown text.

We will now use the saveAs() method of the FileSaver module to save the file to the system. This method uses two parameters, the first one is the blob object that has to be saved and the second one is the file name that should be used to save the file. We can get the file name by using the fileName variable and pass the blob object we created earlier. 

We can bind these functions with the respective onClick event handlers of the two buttons we have created. Clicking on the buttons will open a Save As dialog on the browser with the given filename and the user can choose to save the file as required.

App.js:

Javascript




import { useState } from 'react';
  
// Import the required modules
import MarkdownIt from 'markdown-it';
import FileSaver from 'file-saver';
  
import './App.css';
  
function App() {
  
  // Create the states that will be used 
  // through the application
  // This state will be used for the Markdown text
  let [markdownText, setMarkDownText] = useState("");
  
  // This state will be used for the rendered HTML
  let [renderedHTML, setRenderedHTML] = useState("");
  
  // This state will be used for the filename while saving
  let [fileName, setFileName] = useState("untitled-note");
  
  // Create a function that will be invoked
  // whenever the user modifies the content
  // in the textarea
  function handleTextInput(e) {
  
    // Change the text of the markdown side
    setMarkDownText(e.target.value);
  
    // Initialize a MarkdownIt instance
    let md = new MarkdownIt();
  
    // Render out the markdown to HTML using
    // the render() method
    let renderedHTML = md.render(e.target.value);
  
    // Change the markdown's side to the rendered HTML
    setRenderedHTML(renderedHTML);
  }
  
  // Create a function download the rendered HTML
  function saveHTML() {
  
    // Create a new Blob of the type 'text/html'
    // using the rendered HTML content
    let blobFile = new Blob([renderedHTML], {
      type: "text/html"
    });
  
    // Save the file using the given file name
    FileSaver.saveAs(blobFile, fileName);
  }
  
  // Create a function download the Markdown text
  function saveMarkdown() {
  
    // Create a new Blob of the type 'text'
    // using the markdown content
    let blobFile = new Blob([markdownText], {
      type: "text"
    });
  
    // Save the file using the given file name
    FileSaver.saveAs(blobFile, fileName);
  }
  
  return (
    <div className="container">
      <h2 className="app-heading text-center
                     display-4 my-3">
        React Markdown Notes
      </h2>
      <div className="row">
        <div className="col col-sm-12 col-md-6">
  
          {/* Card for the markdown editor */}
          <div className="card bg-light markdown-editor">
            <h4 className="card-title text-center">
              Markdown
            </h4>
            <div className="card-body">
  
              {/* Textarea for the markdown editor */}
              <textarea
                className="form-control markdown-textarea"
                rows={20}
                value={markdownText}
                onChange={handleTextInput}
              ></textarea>
            </div>
          </div>
        </div>
        <div className="col col-sm-12 col-md-6">
  
          {/* Card for the markdown editor */}
          <div className="card bg-light rendered-output">
            <h4 className="card-title text-center">
              Output
            </h4>
  
            {/* Textarea for the markdown editor */}
            <div className="card-body">
              <div
  
                // Change the HTML to be displayed according
                // to the render produced by MarkdownIt
                dangerouslySetInnerHTML={{ __html: renderedHTML }}
                className="rendered-html-output"
              >
              </div>
            </div>
          </div>
        </div>
      </div>
  
      <div className="row">
        <div className="col col-sm-12">
  
          {/* Card for the save files option */}
          <div className="card bg-light my-3">
            <div className="card-body">
              <h4>Save Content</h4>
              <div className="input-group">
                <input
                  type="text"
                  className="form-control"
                  placeholder="File name"
                  aria-label="File name"
                  value={fileName}
                  onChange={fname => setFileName(fname.target.value)}
                />
  
                {/* Buttons for saving the text */}
                <div className="input-group-append">
                  <button className="btn btn-primary" 
                          type="button" 
                          onClick={saveMarkdown}>
                            Save Markdown
                  </button>
                  <button className="btn btn-primary" 
                          type="button" 
                          onClick={saveHTML}>
                            Save HTML
                  </button>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}
  
export default App;


Step 6: Running and Building the application

We can run this application by using the following command. This will start React’s development server that can be used for debugging our application.

npm run start

We can also build this application so that it could be hosted on any platform that supports static file hosting. We can build our application using the following command to run the project:

npm run build

Output:

Source Code: https://github.com/sayantanm19/react-markdown-notes-gfg

Whether you’re preparing for your first job interview or aiming to upskill in this ever-evolving tech landscape, neveropen Courses are your key to success. We provide top-quality content at affordable prices, all geared towards accelerating your growth in a time-bound manner. Join the millions we’ve already empowered, and we’re here to do the same for you. Don’t miss out – check it out now!

Dominic Rubhabha Wardslaus
Dominic Rubhabha Wardslaushttps://neveropen.dev
infosec,malicious & dos attacks generator, boot rom exploit philanthropist , wild hacker , game developer,
RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments