# Markdown Parser React

A flexible and feature-rich React component for rendering Markdown content with customizable styling, extensive formatting options, and robust typings.

![markdown-parser-react](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0h9v6cfybc92ozlfa65x.png)

![npm](https://img.shields.io/npm/v/markdown-parser-react) ![npm bundle size](https://img.shields.io/bundlephobia/min/markdown-parser-react)

![downloads](https://img.shields.io/npm/dt/markdown-parser-react?color=green&label=downloads&logo=npm)

![stars](https://img.shields.io/github/stars/J3rry320/markdown-parser-react?color=brightgreen&label=stars&logo=github)

[![MIT License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)![Code Coverage](https://img.shields.io/badge/Coverage-95.08%25-brightgreen)

## Features

- Full Markdown syntax support

- **Token-based React Renderer** (No `dangerouslySetInnerHTML`)

- Customizable styling with CSS classes and inline styles

- **Advanced Component Overrides** for any elements

- HTML sanitization (enabled by default)

- Accessibility support (WCAG compliant)

- Responsive image handling

- Syntax highlighting for code blocks

- Task list support

- Table support with alignment options

- Custom link handling

- Definition lists

- Custom IDs for headers

- Nested list support

- **Mathematical blocks** (`$$`) and inline math (`$`)

- **GitHub-style Alerts** (`> [!NOTE]`)

- **Footnote support** (`[^1]`)

## Installation

Install Markdown Parser React using npm or yarn or pnpm:

```bash
npm  install  markdown-parser-react
# or
yarn  add  markdown-parser-react
# or
pnpm  add  markdown-parser-react
```

## Basic Usage

The basic usage example demonstrates rendering a simple Markdown string:

```jsx
import Markdown from "markdown-parser-react";

function MyComponent() {
  return (
    <Markdown
      content={`# Hello World
This is **markdown** content.`}
    />
  );
}
```

## Advanced Usage

In the advanced usage example, we see how you can configure the component with custom styling, classes, and additional features like task lists, code highlighting, and tables

```jsx
import Markdown from "markdown-parser-react";

function BlogPost() {
  const markdownContent = `
# Welcome to My Blog

> [!TIP]
> This is a modern markdown parser using React nodes!

This is a _formatted_ paragraph with a [link](https://example.com).

- [x] Task 1
- [ ] Task 2

\`\`\`javascript
const hello = "world";
console.log(hello);
\`\`\`

| Column 1 | Column 2 |
|----------|----------|
| Cell 1   | Cell 2   |
`;

  return (
    <Markdown
      content={markdownContent}
      options={{
        customClasses: {
          headings: "blog-heading",
          paragraphs: "blog-paragraph",
        },
        customStyles: {
          headings: {
            color: "#2c3e50",
            fontFamily: "Georgia, serif",
          },
        },
        linkTarget: "_blank",
        sanitizeHtml: true,
        maxNestingLevel: 4,
      }}
      className="blog-content"
      asArticle={true}
      aria={{
        label: "Blog post content",
        describedBy: "blog-description",
      }}
    />
  );
}
```

## Usage with Next.js

If you're using Next.js, you may encounter the _**"Text content does not match server-rendered HTML"**_ error.

To avoid this issue, you can use `next/dynamic` to dynamically import the `Markdown` component, ensuring that it is only rendered on the client-side.

Here's how to use `Markdown` with Next.js:

```jsx

import dynamic from "next/dynamic";

const Markdown = dynamic(
  () => import("markdown-parser-react").then((m) => m),
  { ssr: false }
);

interface MyComponentProps {
  content: string;
  options?: {
    langPrefix?: string;
    linkTarget?: string;
  };
}

export const MyComponent: React.FC<MyComponentProps> = ({
  content,
  options,
}) => {
  return (
    <div>
      <Markdown content={content} options={options} />
    </div>
  );
};
```

By using the `next/dynamic` function and passing `ssr: false`, we ensure that the `Markdown` component is only rendered on the client-side, preventing the mismatch error between server-rendered and client-rendered HTML in Next.js projects.

> [!NOTE]

> Since the latest version uses a token-based React renderer, many hydration issues are naturally mitigated. However, if you use custom components that rely on browser APIs, `next/dynamic` is still recommended.

## Security & Performance: Token-based Rendering

Unlike many markdown parsers that rely on `dangerouslySetInnerHTML`, this package uses a **token-based React renderer**.

- **Safer**: It directly creates React elements (`React.createElement`), making it inherently immune to most XSS attack vectors that target raw HTML injection.

- **Faster**: By avoiding string-to-HTML parsing in the browser, it leverages React's virtual DOM more efficiently.

- **Highly Configurable**: Since every node is a React component, you can intercept and modify any part of the rendering tree.

## Configuration Options

### Props

The `MarkdownProps` interface defines the properties that can be passed into the component. These props allow you to customize the behavior, appearance, and accessibility of the rendered Markdown.

### Component Props

| Prop            | Type            | Description                                        | Example                       |
| :-------------- | :-------------- | :------------------------------------------------- | :---------------------------- |
| **`content`**   | `string`        | The Markdown content to be rendered.               | `content="# Hello"`           |
| **`options`**   | `ParseOptions`  | Configuration options for the parser.              | `options={{ ... }}`           |
| **`className`** | `string`        | Additional CSS class for the container.            | `className="markdown-body"`   |
| **`style`**     | `CSSProperties` | Custom styles for the container.                   | `style={{ padding: 20 }}`     |
| **`asArticle`** | `boolean`       | Wrap in `<article>` instead of `<div>`.            | `asArticle={true}`            |
| **`id`**        | `string`        | Custom ID for the container.                       | `id="post-content"`           |
| **`aria`**      | `object`        | Accessibility attributes (`label`, `describedBy`). | `aria={{ label: 'Article' }}` |

### ParseOptions

The `ParseOptions` interface provides configuration for parsing and rendering the Markdown content. It gives you the ability to customize how the content is parsed and displayed, including applying custom classes, styles, and controlling link behavior.

| Option                | Type               | Description                      | Example                         |
| :-------------------- | :----------------- | :------------------------------- | :------------------------------ |
| **`langPrefix`**      | `string`           | Prefix for code block classes.   | `langPrefix="lang-"`            |
| **`customClasses`**   | `CustomClasses`    | Custom CSS classes for elements. | `{ headings: 'my-h' }`          |
| **`customStyles`**    | `CustomStyles`     | Custom CSS styles for elements.  | `{ paragraphs: { margin: 0 } }` |
| **`linkTarget`**      | `string`           | How links open (`_blank`, etc).  | `linkTarget="_self"`            |
| **`sanitizeHtml`**    | `boolean`          | Sanitize HTML input.             | `sanitizeHtml={false}`          |
| **`maxNestingLevel`** | `number`           | Max list nesting depth.          | `maxNestingLevel={3}`           |
| **`components`**      | `CustomComponents` | Element component overrides.     | `{ h1: MyH1 }`                  |
| **`onRenderMath`**    | `Function`         | Custom LaTeX/TeX renderer.       | `(tex, block) => <Math />`      |
| **`onRenderCode`**    | `Function`         | Custom syntax highlighter.       | `(code, lang) => <High />`      |

## Supported Markdown Features

### Basic Formatting

- Headers (H1-H6)

- Bold and Italic text

- Strikethrough

- Links

- Images

- Blockquotes with citations

- Ordered and unordered lists

- Code blocks with syntax highlighting

### Extended Features

- Task lists with checkboxes

- Tables with alignment options

- Definition lists

- Custom header IDs

- **Math equations** (Inline `$x$` and Block `$$x$$`)

- **GitHub-style Alerts** (`> [!NOTE]`)

- **Footnotes** (`[^1]`)

- Superscript (`^...^`)

- Subscript (`~...~`)

- Highlighted text (`==...==`)

### Code Block Example

````markdown
```javascript
function hello() {
  console.log("Hello, world!");
}
```
````

### Table Example

```markdown
| Left | Center | Right |
| :--- | :----: | ----: |
| L1   |   C1   |    R1 |
```

## Customization

You can customize how the Markdown content is displayed by adjusting the appearance of different elements like headings, paragraphs, links, and more. Below are the options for customizing the styles and classes.

### Custom Classes

You can pass your classNames to customClasses config inside the options object passed as props to the component

| Element           | Type     | Description             |
| :---------------- | :------- | :---------------------- |
| **`headings`**    | `string` | Classes for H1-H6.      |
| **`paragraphs`**  | `string` | Classes for P.          |
| **`lists`**       | `string` | Classes for UL/OL/LI.   |
| **`blockquotes`** | `string` | Classes for BLOCKQUOTE. |
| **`codeBlocks`**  | `string` | Classes for PRE/CODE.   |
| **`tables`**      | `string` | Classes for TABLE.      |
| **`links`**       | `string` | Classes for A.          |
| **`images`**      | `string` | Classes for IMG.        |

### Custom Styles

You can pass your styles to the customStyles property inside the option object.

| Element           | Type            | Description             |
| :---------------- | :-------------- | :---------------------- |
| **`headings`**    | `CSSProperties` | Styles for H1-H6.       |
| **`paragraphs`**  | `CSSProperties` | Styles for P.           |
| **`lists`**       | `CSSProperties` | Styles for lists.       |
| **`blockquotes`** | `CSSProperties` | Styles for blockquotes. |
| **`codeBlocks`**  | `CSSProperties` | Styles for code blocks. |
| **`tables`**      | `CSSProperties` | Styles for tables.      |
| **`links`**       | `CSSProperties` | Styles for links.       |
| **`images`**      | `CSSProperties` | Styles for images.      |

### Advanced Customization: Component Overrides

The `components` prop in `options` allows you to completely replace the default markdown elements with your own React components. This is perfect for integrating with UI libraries like Shadcn UI, MUI, or Radix UI.

```jsx
<Markdown
  content="# Hello World"
  options={{
    components: {
      // Override H1 with a custom styled component
      h1: ({ children }) => (
        <h1 className="text-4xl font-black text-indigo-600 mb-4">{children}</h1>
      ),

      // Use a custom Link component for internal navigation
      a: ({ href, children }) => <CustomLink to={href}>{children}</CustomLink>,

      // Customize image rendering
      img: (props) => (
        <img {...props} className="rounded-lg shadow-xl" loading="lazy" />
      ),

      // Override mathematical rendering (e.g. using KaTeX)
      math: ({ content, isBlock }) => (
        <MyMathRenderer tex={content} block={isBlock} />
      ),

      // Customize GitHub Alerts
      alert: ({ type, children }) => (
        <div
          className={`alert alert-${type.toLowerCase()} p-4 border-l-4 my-4 bg-gray-50`}
        >
          <span className="font-bold uppercase mr-2">{type}:</span>
          {children}
        </div>
      ),
    },
  }}
/>
```

```jsx
<Markdown
  content={content}
  options={{
    onRenderCode: (code, language) => {
      return <div className="p-4 bg-slate-900 text-white rounded">{code}</div>;
    },
  }}
/>
```

## Pro Examples

Here are some real-world examples of how to extend `markdown-parser-react` for your specific needs.

### Mathematical Rendering (with KaTeX)

Integrate [KaTeX](https://katex.org/) for beautiful, high-performance mathematical typesetting.

```jsx
import "katex/dist/katex.min.css";
import { InlineMath, BlockMath } from "react-katex";

<Markdown
  content="The equation is $E=mc^2$"
  options={{
    onRenderMath: (content, isBlock) =>
      isBlock ? <BlockMath math={content} /> : <InlineMath math={content} />,
  }}
/>;
```

### Advanced Syntax Highlighting (with PrismJS)

Use `PrismJS` or `react-syntax-highlighter` for professional-grade code blocks.

```jsx
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism";

<Markdown
  content={myCode}
  options={{
    onRenderCode: (code, language) => (
      <SyntaxHighlighter language={language} style={oneDark}>
        {code}
      </SyntaxHighlighter>
    ),
  }}
/>;
```

### Custom Alert Components

Theme-aware alerts with custom icons using the `alert` component override.

```jsx
const CustomAlert = ({ type, children }) => {
  const icons = {
    NOTE: "📝",
    TIP: "💡",
    IMPORTANT: "📌",
    WARNING: "⚠️",
    CAUTION: "🛑",
  };

  return (
    <div className={`my-alert alert-${type.toLowerCase()}`}>
      <span className="icon">{icons[type] || icons.NOTE}</span>
      <div className="content">{children}</div>
    </div>
  );
};

<Markdown
  content={"> [!TIP]\n> Always use specific keys for lists!"}
  options={{
    components: {
      alert: CustomAlert,
    },
  }}
/>;
```

### Managing Footnotes

By default, footnotes are rendered as links. You can capture them in a separate container for a traditional "bottom-of-page" feel.

```jsx
/*
Markdown Input:
This is a statement[^1].

[^1]: This is the source.
*/

// In your CSS (Tailwind or standard CSS):
.footnote-ref {
  vertical-align: super;
  font-size: 0.75em;
  color: #4f46e5; /* indigo-600 */
  margin-left: 0.1rem;
  text-decoration: none;
}

.footnote-ref:hover {
  text-decoration: underline;
}

#footnotes {
  border-top: 1px solid #e5e7eb; /* slate-200 */
  margin-top: 3rem;
  padding-top: 1.5rem;
  font-size: 0.875em;
  color: #4b5563; /* slate-600 */
}

```

## Default Styling

The component comes with default styles that can be imported:

```jsx
import { createGlobalStyle } from "styled-components";
import { defaultStyles } from "markdown-parser-react";

// In your global CSS or styled-components
const GlobalStyle = createGlobalStyle`
  /* Inject base markdown styles */
  ${defaultStyles}
`;

export default GlobalStyle;
```

## Accessibility

The component follows WCAG guidelines and provides:

- Semantic HTML structure

- ARIA attributes support

- Keyboard navigation for interactive elements

- Screen reader-friendly content structure

## Browser Support

- Chrome (latest)

- Firefox (latest)

- Safari (latest)

- Edge (latest)

- IE11 (with appropriate polyfills)

## Contributing

Contributions are welcome! Please read our [contributing guidelines](https://github.com/J3rry320/markdown-parser-react/blob/main/CONTRIBUTING.md) for more information.

## Issues

If you encounter any issues or have suggestions for improvements, please report them on the [GitHub Issues page](https://github.com/J3rry320/markdown-parser-react/issues)

## Work with Us

[Code Media Labs](https://codemedialabs.in) is a multidisciplinary studio uniting Design, Media, Software, and Legal expertise to create thoughtful, future-ready digital experiences. We partner with brands to strengthen their identity, streamline operations, and unlock long-term growth through functional and innovative solutions.

## License

This project is licensed under the MIT License. See the [LICENSE file](https://github.com/J3rry320/markdown-parser-react/blob/main/LICENSE) for details.

Happy Coding Everyone!
