Hello folks! Ever wanted to build your own Markdown Previewer using ReactJS? Well, today is your lucky day! I recently built one using ReactJS, Twin Macro, and styled-components, and I thought it would be fun to create a step-by-step guide to help you build one too. So, grab a cup of coffee (or tea, if that's your thing), and let's get started!
Step 1: Setting Up the Basic React App
First things first, we need to set up a basic React app. If you haven't already installed Node.js and npm, make sure to install them before proceeding.
Open your terminal and run the following command to create a new React app:
npx create-react-app markdown-previewer
Step 2: Installing Packages
Next, we need to install the necessary packages. Navigate to your project folder and run the following commands to install the required packages:
npm install styled-components twin.macro marked dompurify
Step 3: Importing important packages
Now that we have our packages installed, let's understand the import statements in our code.
import React, { useState } from 'react';
import styled from 'styled-components';
import tw from 'twin.macro';
import {marked} from 'marked';
import DOMPurify from 'dompurify';
- React, { useState } are imported from the 'react' package. The useState hook is used to manage the state in our functional component.
- styled is imported from the 'styled-components' package. This allows us to write actual CSS code to style our components.
- tw is imported from 'twin.macro'. This is a utility-first CSS framework for rapidly building custom designs.
- marked is imported from the 'marked' package. This is a full-featured markdown parser and compiler.
- DOMPurify is imported from the 'dompurify' package. This is used to sanitize our HTML and prevent XSS attacks.
Step 4: Styling Our Components
Now that we have our import statements ready, let's style our components using styled-components
and twin.macro
.
const Container = styled.div`
${tw`container mx-auto mt-10 mb-20`}
`;
const Header = styled.div`
${tw`text-center text-xl font-bold`}
`;
const Row = styled.div`
${tw`flex mt-10`}
`;
const Column = styled.div`
${tw`flex-1 mx-5 h-[35rem] w-[35rem]`}
`;
const Textarea = styled.textarea`
${tw`w-full h-full p-3 border rounded-md transition-all duration-300 ease-in-out`}
&:focus {
${tw`border-blue-500 outline-none shadow-outline`}
}
`;
const Preview = styled.div`
${tw`h-full w-full overflow-y-scroll overflow-x-scroll p-3 border rounded-md bg-gray-100`}`;
Here, we have defined a Container, Header, Row, Column, Textarea, and Preview using styled-components
and twin.macro
. The Container is a div
element with some margin and padding. The Header is a div
element with centered text and a bold font. The Row is a div
element with flex display and some margin at the top. The Column is a div
element with flex-1, margin, height, and width defined. The Textarea is a textarea
element with full width, full height, padding, border, rounded corners, and transition effects. The Preview is a div
element with full height, full width, overflow scroll, padding, border, rounded corners, and a gray background.
Step 5: Handling Markdown Input and Preview
Now that we have our components styled, let's handle the markdown input and preview:
const [markdown, setMarkdown] = useState('');
const handleMarkdownChange = (e) => {
setMarkdown(e.target.value);
};
const getSanitizedHTML = () => {
const rawHTML = marked(markdown);
return DOMPurify.sanitize(rawHTML);
}
In this block of code, we have defined a markdown
state and a setMarkdown
function to update the state. The handleMarkdownChange
function is used to update the markdown state whenever the user types in the Textarea
. The getSanitizedHTML
function is used to convert the markdown text to HTML and sanitize it using DOMPurify
to prevent XSS attacks.
Step 6: Rendering the Components
Finally, let's render our components. Here is the return statement for our component:
return (
<Container>
<Header>Markdown Previewer</Header>
<Row>
<Column>
<Header>Markdown Input</Header>
<Textarea value={markdown} onChange={handleMarkdownChange} />
</Column>
<Column>
<Header>Preview</Header>
<Preview dangerouslySetInnerHTML={{ __html: getSanitizedHTML()}} />
</Column>
</Row>
</Container>
);
In this return
statement, we have a Container with a Header, a Row with two Columns, each with a Header, a Textarea for the markdown input, and a Preview for the markdown preview. The Textarea uses the markdown state as its value and the handleMarkdownChange
function as its onChange
handler. The Preview uses the getSanitizedHTML
function to set its innerHTML
.
And that's it! You have successfully built a Markdown Previewer using ReactJS, Twin Macro, and styled-components. Congratulations! π.
And here's the complete component code:
import React, { useState } from 'react';
import styled from 'styled-components';
import tw from 'twin.macro';
import {marked} from 'marked';
import DOMPurify from 'dompurify';
const Container = styled.div`
${tw`container mx-auto mt-10 mb-20`}
`;
const Header = styled.div`
${tw`text-center text-xl font-bold`}
`;
const Row = styled.div`
${tw`flex mt-10`}
`;
const Column = styled.div`
${tw`flex-1 mx-5 h-[35rem] w-[35rem]`}
`;
const Textarea = styled.textarea`
${tw`w-full h-full p-3 border rounded-md transition-all duration-300 ease-in-out`}
&:focus {
${tw`border-blue-500 outline-none shadow-outline`}
}
`;
const Preview = styled.div`
${tw`h-full w-full overflow-y-scroll overflow-x-scroll p-3 border rounded-md bg-gray-100`}
`;
const MarkdownPreviewer = () => {
const [markdown, setMarkdown] = useState('');
const handleMarkdownChange = (e) => {
setMarkdown(e.target.value);
};
const getSanitizedHTML = () => {
const rawHTML = marked(markdown);
return DOMPurify.sanitize(rawHTML);
}
return (
<Container>
<Header>Markdown Previewer</Header>
<Row>
<Column>
<Header>Markdown Input</Header>
<Textarea value={markdown} onChange={handleMarkdownChange} />
</Column>
<Column>
<Header>Preview</Header>
<Preview dangerouslySetInnerHTML={{ __html: getSanitizedHTML()}} />
</Column>
</Row>
</Container>
);
};
export default MarkdownPreviewer;
Here's the final product π:
Top comments (0)