React-Markdown - Complete Guide

React-markdown is a popular React component library that allows you to render markdown content in your React applications. This comprehensive guide will teach you everything you need to know about using react-markdown.

What is React-Markdown?

React-markdown is a markdown renderer for React that converts markdown text into React components. It's built on top of the remark markdown parser and provides a safe, extensible way to render markdown in React applications.

Key Features:

Installation

Install react-markdown using npm or yarn:

Using npm:

npm install react-markdown

Using yarn:

yarn add react-markdown

Basic Usage

The simplest way to use react-markdown is to import the component and pass markdown text as children:

Example:

import React from 'react';
import ReactMarkdown from 'react-markdown';

function App() {
  const markdown = `# Hello World
  
This is **bold** text and this is *italic* text.

- List item 1
- List item 2
- List item 3
`;

  return (
    <ReactMarkdown>{markdown}</ReactMarkdown>
  );
}

export default App;

Using Props

You can also pass markdown content using the children prop:

Example:

import ReactMarkdown from 'react-markdown';

function MarkdownRenderer({ content }) {
  return (
    <ReactMarkdown children={content} />
  );
}

Custom Components

One of the powerful features of react-markdown is the ability to customize how markdown elements are rendered:

Example:

import ReactMarkdown from 'react-markdown';

const components = {
  h1: ({node, ...props}) => <h1 style={{color: 'blue'}} {...props} />,
  p: ({node, ...props}) => <p style={{fontSize: '18px'}} {...props} />,
  code: ({node, inline, ...props}) => 
    inline 
      ? <code style={{background: '#f4f4f4'}} {...props} />
      : <pre><code {...props} /></pre>,
};

function CustomMarkdown({ content }) {
  return (
    <ReactMarkdown components={components}>
      {content}
    </ReactMarkdown>
  );
}

Common Props

Here are some commonly used props in react-markdown:

Using Plugins

React-markdown supports plugins to extend functionality. Here's an example with syntax highlighting:

Example with Syntax Highlighting:

import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';

function MarkdownWithSyntaxHighlighting({ content }) {
  return (
    <ReactMarkdown
      remarkPlugins={[remarkGfm]}
      components={{
        code({node, inline, className, children, ...props}) {
          const match = /language-(\w+)/.exec(className || '');
          return !inline && match ? (
            <SyntaxHighlighter
              style={vscDarkPlus}
              language={match[1]}
              PreTag="div"
              {...props}
            >
              {String(children).replace(/\n$/, '')}
            </SyntaxHighlighter>
          ) : (
            <code className={className} {...props}>
              {children}
            </code>
          );
        }
      }}
    >
      {content}
    </ReactMarkdown>
  );
}

GitHub Flavored Markdown

To support GitHub Flavored Markdown (GFM) features like tables, strikethrough, and task lists, use the remark-gfm plugin:

Installation:

npm install remark-gfm

Usage:

import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';

function GFMExample({ content }) {
  return (
    <ReactMarkdown remarkPlugins={[remarkGfm]}>
      {content}
    </ReactMarkdown>
  );
}

Security Considerations

React-markdown is safe by default and protects against XSS attacks. However, if you're allowing user-generated content, consider:

Performance Optimization

For better performance with react-markdown:

TypeScript Support

React-markdown has excellent TypeScript support. Install types if needed:

TypeScript Example:

import ReactMarkdown from 'react-markdown';
import { Components } from 'react-markdown';

interface MarkdownProps {
  content: string;
}

const components: Components = {
  h1: ({children}) => <h1 className="title">{children}</h1>,
  p: ({children}) => <p className="paragraph">{children}</p>,
};

function TypedMarkdown({ content }: MarkdownProps) {
  return (
    <ReactMarkdown components={components}>
      {content}
    </ReactMarkdown>
  );
}

Common Use Cases

Best Practices

  1. Sanitize Input: Always sanitize user-generated markdown
  2. Use Plugins Wisely: Only include plugins you actually need
  3. Customize Components: Use custom components for consistent styling
  4. Test Rendering: Test how markdown renders across different scenarios
  5. Consider Accessibility: Ensure custom components are accessible

Advanced Customization

Custom Link Components

Create custom link components with additional functionality:

Example:

import ReactMarkdown from 'react-markdown';

const components = {
  a: ({node, href, children, ...props}) => (
    <a 
      href={href} 
      target="_blank" 
      rel="noopener noreferrer"
      className="custom-link"
      {...props}
    >
      {children}
    </a>
  ),
};

function CustomLinks({ content }) {
  return (
    <ReactMarkdown components={components}>
      {content}
    </ReactMarkdown>
  );
}

Custom Image Components

Add lazy loading, error handling, or other features to images:

Example:

const components = {
  img: ({node, src, alt, ...props}) => (
    <img 
      src={src} 
      alt={alt}
      loading="lazy"
      onError={(e) => {
        e.target.src = '/placeholder.png';
      }}
      {...props}
    />
  ),
};

Error Handling

Handle errors gracefully when rendering markdown:

Example:

import ReactMarkdown from 'react-markdown';
import { useState } from 'react';

function SafeMarkdownRenderer({ content }) {
  const [error, setError] = useState(null);
  
  try {
    return (
      <ReactMarkdown>
        {content}
      </ReactMarkdown>
    );
  } catch (err) {
    return (
      <div className="error">
        Error rendering markdown: {err.message}
      </div>
    );
  }
}

Integration with State Management

Integrate react-markdown with state management libraries:

Example with Redux:

import ReactMarkdown from 'react-markdown';
import { useSelector } from 'react-redux';

function MarkdownFromStore() {
  const markdownContent = useSelector(state => state.content.markdown);
  
  return (
    <ReactMarkdown>
      {markdownContent}
    </ReactMarkdown>
  );
}

Server-Side Rendering (SSR)

Use react-markdown with Next.js or other SSR frameworks:

Next.js Example:

import ReactMarkdown from 'react-markdown';
import { readFileSync } from 'fs';

export async function getStaticProps() {
  const content = readFileSync('content.md', 'utf-8');
  return { props: { content } };
}

export default function Page({ content }) {
  return (
    <ReactMarkdown>
      {content}
    </ReactMarkdown>
  );
}

Testing react-markdown Components

Test your react-markdown components effectively:

Testing Example:

import { render, screen } from '@testing-library/react';
import ReactMarkdown from 'react-markdown';

test('renders markdown correctly', () => {
  const markdown = '# Hello World\n\nThis is a test.';
  render(<ReactMarkdown>{markdown}</ReactMarkdown>);
  
  expect(screen.getByText('Hello World')).toBeInTheDocument();
  expect(screen.getByText('This is a test.')).toBeInTheDocument();
});

Common Plugins and Extensions

remark-breaks

Convert line breaks to `
` tags:

Example:

import ReactMarkdown from 'react-markdown';
import remarkBreaks from 'remark-breaks';

<ReactMarkdown remarkPlugins={[remarkBreaks]}>
  {content}
</ReactMarkdown>

remark-math and rehype-katex

Render mathematical equations:

Example:

import ReactMarkdown from 'react-markdown';
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
import 'katex/dist/katex.min.css';

<ReactMarkdown
  remarkPlugins={[remarkMath]}
  rehypePlugins={[rehypeKatex]}
>
  {content}
</ReactMarkdown>

Troubleshooting Common Issues

Markdown Not Rendering

If markdown isn't rendering:

Styling Issues

If styles aren't applying:

Performance Issues

If rendering is slow:

Migration from Other Libraries

Migrating from marked

If migrating from marked library:

Best Practices Recap

Key takeaways for using react-markdown effectively:

  1. Security First: Always sanitize user-generated content
  2. Customize Wisely: Use custom components for consistent styling
  3. Plugin Management: Only include plugins you actually need
  4. Performance: Optimize for your specific use case
  5. Accessibility: Ensure custom components are accessible
  6. Testing: Test with various markdown inputs
  7. Documentation: Document custom components and configurations

Resources

For more information about react-markdown:

Advanced Component Patterns

Custom Blockquote Components

Create styled blockquotes with icons or special formatting:

Example:

const components = {
  blockquote: ({node, children, ...props}) => (
    <blockquote className="custom-blockquote" {...props}>
      <div className="quote-icon">❝</div>
      <div className="quote-content">{children}</div>
    </blockquote>
  ),
};

Custom Table Components

Enhance tables with sorting, filtering, or responsive design:

Example:

const components = {
  table: ({node, children, ...props}) => (
    <div className="table-wrapper">
      <table className="markdown-table" {...props}>
        {children}
      </table>
    </div>
  ),
  thead: ({node, children, ...props}) => (
    <thead className="table-header" {...props}>
      {children}
    </thead>
  ),
  tbody: ({node, children, ...props}) => (
    <tbody className="table-body" {...props}>
      {children}
    </tbody>
  ),
  tr: ({node, children, ...props}) => (
    <tr className="table-row" {...props}>
      {children}
    </tr>
  ),
  th: ({node, children, ...props}) => (
    <th className="table-head" {...props}>
      {children}
    </th>
  ),
  td: ({node, children, ...props}) => (
    <td className="table-cell" {...props}>
      {children}
    </td>
  ),
};

Custom List Components

Style lists with custom icons or animations:

Example:

const components = {
  ul: ({node, children, ...props}) => (
    <ul className="custom-list" {...props}>
      {children}
    </ul>
  ),
  ol: ({node, children, ...props}) => (
    <ol className="custom-ordered-list" {...props}>
      {children}
    </ol>
  ),
  li: ({node, children, ...props}) => (
    <li className="custom-list-item" {...props}>
      <span className="list-marker"></span>
      {children}
    </li>
  ),
};

More Remark Plugins

remark-slug

Add IDs to headings automatically for anchor links:

Installation:

npm install remark-slug

Usage:

import ReactMarkdown from 'react-markdown';
import remarkSlug from 'remark-slug';

<ReactMarkdown remarkPlugins={[remarkSlug]}>
  {content}
</ReactMarkdown>

remark-autolink-headings

Automatically add links to headings:

Installation:

npm install remark-autolink-headings

Usage:

import ReactMarkdown from 'react-markdown';
import remarkSlug from 'remark-slug';
import remarkAutolinkHeadings from 'remark-autolink-headings';

<ReactMarkdown 
  remarkPlugins={[
    remarkSlug,
    [remarkAutolinkHeadings, { behavior: 'wrap' }]
  ]}
>
  {content}
</ReactMarkdown>

remark-emoji

Convert emoji shortcodes to emoji:

Installation:

npm install remark-emoji

Usage:

import ReactMarkdown from 'react-markdown';
import remarkEmoji from 'remark-emoji';

<ReactMarkdown remarkPlugins={[remarkEmoji]}>
  {content}
</ReactMarkdown>

remark-footnotes

Support footnotes in markdown:

Installation:

npm install remark-footnotes

Usage:

import ReactMarkdown from 'react-markdown';
import remarkFootnotes from 'remark-footnotes';

<ReactMarkdown remarkPlugins={[remarkFootnotes]}>
  {content}
</ReactMarkdown>

remark-images

Enhance image handling with size attributes:

Installation:

npm install remark-images

Usage:

import ReactMarkdown from 'react-markdown';
import remarkImages from 'remark-images';

<ReactMarkdown remarkPlugins={[remarkImages]}>
  {content}
</ReactMarkdown>

More Rehype Plugins

rehype-raw

Parse HTML in markdown (use with caution):

Installation:

npm install rehype-raw

Usage:

import ReactMarkdown from 'react-markdown';
import rehypeRaw from 'rehype-raw';

<ReactMarkdown rehypePlugins={[rehypeRaw]}>
  {content}
</ReactMarkdown>

rehype-sanitize

Sanitize HTML to prevent XSS attacks:

Installation:

npm install rehype-sanitize

Usage:

import ReactMarkdown from 'react-markdown';
import rehypeRaw from 'rehype-raw';
import rehypeSanitize from 'rehype-sanitize';

<ReactMarkdown 
  rehypePlugins={[rehypeRaw, rehypeSanitize]}
>
  {content}
</ReactMarkdown>

rehype-highlight

Syntax highlighting for code blocks:

Installation:

npm install rehype-highlight

Usage:

import ReactMarkdown from 'react-markdown';
import rehypeHighlight from 'rehype-highlight';
import 'highlight.js/styles/github.css';

<ReactMarkdown rehypePlugins={[rehypeHighlight]}>
  {content}
</ReactMarkdown>

rehype-slug

Add slug IDs to headings (rehype version):

Installation:

npm install rehype-slug

Usage:

import ReactMarkdown from 'react-markdown';
import rehypeSlug from 'rehype-slug';

<ReactMarkdown rehypePlugins={[rehypeSlug]}>
  {content}
</ReactMarkdown>

Real-World Examples

Blog Post Renderer

Complete blog post component with metadata and styling:

Example:

import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import remarkSlug from 'remark-slug';
import remarkAutolinkHeadings from 'remark-autolink-headings';

function BlogPost({ content, title, date, author }) {
  const components = {
    h1: ({node, ...props}) => (
      <h1 className="blog-title" {...props} />
    ),
    h2: ({node, ...props}) => (
      <h2 className="blog-heading" {...props} />
    ),
    img: ({node, src, alt, ...props}) => (
      <img 
        src={src} 
        alt={alt}
        className="blog-image"
        loading="lazy"
        {...props}
      />
    ),
    a: ({node, href, children, ...props}) => (
      <a 
        href={href}
        target="_blank"
        rel="noopener noreferrer"
        className="blog-link"
        {...props}
      >
        {children}
      </a>
    ),
  };

  return (
    <article className="blog-post">
      <header className="blog-header">
        <h1>{title}</h1>
        <div className="blog-meta">
          <span>By {author}</span>
          <span>{date}</span>
        </div>
      </header>
      <ReactMarkdown
        remarkPlugins={[
          remarkGfm,
          remarkSlug,
          [remarkAutolinkHeadings, { behavior: 'wrap' }]
        ]}
        components={components}
      >
        {content}
      </ReactMarkdown>
    </article>
  );
}

Documentation Site Component

Documentation renderer with table of contents:

Example:

import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import remarkSlug from 'remark-slug';
import { useState, useEffect } from 'react';

function Documentation({ content }) {
  const [headings, setHeadings] = useState([]);

  useEffect(() => {
    const headingElements = document.querySelectorAll('h2, h3');
    const headingData = Array.from(headingElements).map(el => ({
      id: el.id,
      text: el.textContent,
      level: el.tagName.toLowerCase(),
    }));
    setHeadings(headingData);
  }, [content]);

  return (
    <div className="documentation-container">
      <aside className="table-of-contents">
        <h3>Table of Contents</h3>
        <ul>
          {headings.map(heading => (
            <li key={heading.id} className={`toc-${heading.level}`}>
              <a href={`#${heading.id}`}>{heading.text}</a>
            </li>
          ))}
        </ul>
      </aside>
      <main className="documentation-content">
        <ReactMarkdown
          remarkPlugins={[remarkGfm, remarkSlug]}
        >
          {content}
        </ReactMarkdown>
      </main>
    </div>
  );
}

Comment System

User comment renderer with sanitization:

Example:

import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import rehypeSanitize from 'rehype-sanitize';

function CommentRenderer({ content, author, timestamp }) {
  const components = {
    // Disable images in comments for security
    img: () => null,
    // Limit link functionality
    a: ({node, href, children, ...props}) => {
      const isExternal = href?.startsWith('http');
      return (
        <a
          href={href}
          target={isExternal ? '_blank' : undefined}
          rel={isExternal ? 'noopener noreferrer' : undefined}
          className="comment-link"
          {...props}
        >
          {children}
        </a>
      );
    },
  };

  return (
    <div className="comment">
      <div className="comment-header">
        <strong>{author}</strong>
        <time>{timestamp}</time>
      </div>
      <div className="comment-body">
        <ReactMarkdown
          remarkPlugins={[remarkGfm]}
          rehypePlugins={[rehypeSanitize]}
          components={components}
          allowedElements={['p', 'strong', 'em', 'code', 'a', 'ul', 'ol', 'li']}
        >
          {content}
        </ReactMarkdown>
      </div>
    </div>
  );
}

Markdown Editor with Preview

Live markdown editor with split view:

Example:

import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { useState } from 'react';

function MarkdownEditor() {
  const [markdown, setMarkdown] = useState('# Start writing...\n\nYour markdown here.');

  return (
    <div className="markdown-editor">
      <div className="editor-pane">
        <textarea
          value={markdown}
          onChange={(e) => setMarkdown(e.target.value)}
          className="markdown-input"
          placeholder="Write your markdown here..."
        />
      </div>
      <div className="preview-pane">
        <ReactMarkdown remarkPlugins={[remarkGfm]}>
          {markdown}
        </ReactMarkdown>
      </div>
    </div>
  );
}

Performance Optimization Techniques

Memoization

Memoize components and content to prevent unnecessary re-renders:

Example:

import ReactMarkdown from 'react-markdown';
import { useMemo, memo } from 'react';

const MemoizedMarkdown = memo(ReactMarkdown);

function OptimizedRenderer({ content }) {
  const memoizedContent = useMemo(() => content, [content]);
  
  return (
    <MemoizedMarkdown>
      {memoizedContent}
    </MemoizedMarkdown>
  );
}

Code Splitting

Lazy load react-markdown for better initial load time:

Example:

import { lazy, Suspense } from 'react';

const ReactMarkdown = lazy(() => import('react-markdown'));

function LazyMarkdown({ content }) {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <ReactMarkdown>{content}</ReactMarkdown>
    </Suspense>
  );
}

Virtual Scrolling

For very long documents, consider virtual scrolling:

Example:

import ReactMarkdown from 'react-markdown';
import { FixedSizeList } from 'react-window';

function VirtualizedMarkdown({ content }) {
  // Split content into sections
  const sections = content.split('\n\n');
  
  return (
    <FixedSizeList
      height={600}
      itemCount={sections.length}
      itemSize={100}
    >
      {({ index, style }) => (
        <div style={style}>
          <ReactMarkdown>{sections[index]}</ReactMarkdown>
        </div>
      )}
    </FixedSizeList>
  );
}

Accessibility Best Practices

ARIA Labels

Add proper ARIA labels to custom components:

Example:

const components = {
  img: ({node, src, alt, ...props}) => (
    <img
      src={src}
      alt={alt || 'Image'}
      role="img"
      aria-label={alt || 'Image'}
      {...props}
    />
  ),
  a: ({node, href, children, ...props}) => (
    <a
      href={href}
      aria-label={`Link to ${children}`}
      {...props}
    >
      {children}
    </a>
  ),
};

Keyboard Navigation

Ensure custom interactive components support keyboard navigation:

Example:

const components = {
  a: ({node, href, children, ...props}) => (
    <a
      href={href}
      tabIndex={0}
      onKeyDown={(e) => {
        if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault();
          window.location.href = href;
        }
      }}
      {...props}
    >
      {children}
    </a>
  ),
};

Advanced Configuration

Custom Parser Options

Configure remark and rehype parsers:

Example:

import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { remark } from 'remark';
import { rehype } from 'rehype';

const processor = remark()
  .use(remarkGfm)
  .use(/* custom plugins */);

<ReactMarkdown
  remarkPlugins={[remarkGfm]}
  remarkRehypeOptions={{
    allowDangerousHtml: false,
    clobberPrefix: 'user-content-',
  }}
>
  {content}
</ReactMarkdown>

Conditional Rendering

Conditionally render different components based on content:

Example:

import ReactMarkdown from 'react-markdown';

function ConditionalRenderer({ content, allowImages = true }) {
  const components = {
    img: allowImages 
      ? ({node, ...props}) => <img {...props} />
      : () => null,
  };

  return (
    <ReactMarkdown components={components}>
      {content}
    </ReactMarkdown>
  );
}

Integration Examples

With React Router

Render markdown content from route parameters:

Example:

import ReactMarkdown from 'react-markdown';
import { useParams } from 'react-router-dom';
import { useEffect, useState } from 'react';

function MarkdownPage() {
  const { slug } = useParams();
  const [content, setContent] = useState('');

  useEffect(() => {
    fetch(`/content/${slug}.md`)
      .then(res => res.text())
      .then(text => setContent(text));
  }, [slug]);

  return (
    <ReactMarkdown>{content}</ReactMarkdown>
  );
}

With GraphQL

Fetch and render markdown from GraphQL API:

Example:

import ReactMarkdown from 'react-markdown';
import { useQuery } from '@apollo/client';
import { gql } from '@apollo/client';

const GET_POST = gql`
  query GetPost($id: ID!) {
    post(id: $id) {
      content
      title
    }
  }
`;

function PostPage({ postId }) {
  const { data, loading } = useQuery(GET_POST, {
    variables: { id: postId }
  });

  if (loading) return <div>Loading...</div>;

  return (
    <div>
      <h1>{data.post.title}</h1>
      <ReactMarkdown>{data.post.content}</ReactMarkdown>
    </div>
  );
}

With Contentful CMS

Render markdown from Contentful:

Example:

import ReactMarkdown from 'react-markdown';
import { useEffect, useState } from 'react';
import * as contentful from 'contentful';

const client = contentful.createClient({
  space: 'your-space-id',
  accessToken: 'your-access-token'
});

function ContentfulPost({ entryId }) {
  const [content, setContent] = useState('');

  useEffect(() => {
    client.getEntry(entryId)
      .then(entry => setContent(entry.fields.body));
  }, [entryId]);

  return (
    <ReactMarkdown>{content}</ReactMarkdown>
  );
}

Common Patterns and Solutions

Handling Code Blocks with Copy Button

Add copy functionality to code blocks:

Example:

import ReactMarkdown from 'react-markdown';
import { useState } from 'react';

function CodeBlock({ children, className }) {
  const [copied, setCopied] = useState(false);
  const code = String(children).replace(/\n$/, '');

  const copyToClipboard = () => {
    navigator.clipboard.writeText(code);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };

  return (
    <div className="code-block-wrapper">
      <button onClick={copyToClipboard} className="copy-button">
        {copied ? 'Copied!' : 'Copy'}
      </button>
      <pre className={className}>
        <code>{code}</code>
      </pre>
    </div>
  );
}

const components = {
  code: ({node, inline, className, children, ...props}) => {
    return inline ? (
      <code className={className} {...props}>
        {children}
      </code>
    ) : (
      <CodeBlock className={className} {...props}>
        {children}
      </CodeBlock>
    );
  },
};

<ReactMarkdown components={components}>
  {content}
</ReactMarkdown>

Adding Line Numbers to Code Blocks

Display line numbers in code blocks:

Example:

function CodeBlock({ children, className }) {
  const code = String(children).replace(/\n$/, '');
  const lines = code.split('\n');

  return (
    <pre className={className}>
      <code>
        {lines.map((line, i) => (
          <span key={i} className="code-line">
            <span className="line-number">{i + 1}</span>
            <span className="line-content">{line}</span>
          </span>
        ))}
      </code>
    </pre>
  );
}

Lazy Loading Images

Implement lazy loading for images in markdown:

Example:

import { useState, useRef, useEffect } from 'react';

function LazyImage({ src, alt, ...props }) {
  const [isLoaded, setIsLoaded] = useState(false);
  const imgRef = useRef();

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsLoaded(true);
          observer.disconnect();
        }
      },
      { threshold: 0.1 }
    );

    if (imgRef.current) {
      observer.observe(imgRef.current);
    }

    return () => observer.disconnect();
  }, []);

  return (
    <div ref={imgRef} className="lazy-image-container">
      {isLoaded ? (
        <img src={src} alt={alt} {...props} />
      ) : (
        <div className="image-placeholder">Loading...</div>
      )}
    </div>
  );
}

const components = {
  img: ({node, src, alt, ...props}) => (
    <LazyImage src={src} alt={alt} {...props} />
  ),
};

Version Compatibility

React 18 Support

React-markdown v8+ fully supports React 18 with concurrent features:

Example:

// React 18 with Suspense
import { Suspense } from 'react';
import ReactMarkdown from 'react-markdown';

function App() {
  return (
    <Suspense fallback={<div>Loading markdown...</div>}>
      <ReactMarkdown>{content}</ReactMarkdown>
    </Suspense>
  );
}

React 17 Support

For React 17, use react-markdown v7 or earlier:

Installation:

npm install react-markdown@7

Additional Tips and Tricks

Extracting Text from Markdown

Extract plain text from markdown for previews:

Example:

import { remark } from 'remark';
import stripMarkdown from 'strip-markdown';

async function extractText(markdown) {
  const result = await remark()
    .use(stripMarkdown)
    .process(markdown);
  return String(result);
}

Validating Markdown

Validate markdown before rendering:

Example:

import { remark } from 'remark';

async function validateMarkdown(markdown) {
  try {
    await remark().process(markdown);
    return { valid: true };
  } catch (error) {
    return { valid: false, error: error.message };
  }
}

Custom Markdown Extensions

Create custom markdown syntax with remark plugins:

Example:

import { visit } from 'unist-util-visit';

function remarkCustomAlert() {
  return (tree) => {
    visit(tree, 'text', (node) => {
      // Custom processing logic
      if (node.value.includes(':::alert')) {
        // Transform custom syntax
      }
    });
  };
}

<ReactMarkdown remarkPlugins={[remarkCustomAlert]}>
  {content}
</ReactMarkdown>

Conclusion

React-markdown is an essential tool for any React developer working with markdown content. Its flexibility, security features, and extensive plugin ecosystem make it the go-to solution for rendering markdown in React applications.

Whether you're building a blog, documentation site, comment system, or content management platform, react-markdown provides the tools you need to render beautiful, formatted content from markdown text. With proper customization, security considerations, and performance optimization, you can create exceptional user experiences.

Start using react-markdown in your React applications today and unlock the power of markdown rendering in your projects!