Coder Social home page Coder Social logo

uiwjs / react-markdown-preview Goto Github PK

View Code? Open in Web Editor NEW
268.0 5.0 47.0 121.93 MB

React component preview markdown text in web browser. The minimal amount of CSS to replicate the GitHub Markdown style. Support dark-mode/night mode.

Home Page: https://uiwjs.github.io/react-markdown-preview

License: MIT License

TypeScript 48.90% HTML 3.43% Less 47.67%
react markdown markdown-previewer react-markdown react-markdown-previewer react-component github-style markdown-editor editor remark

react-markdown-preview's Introduction

React Markdown Preview

Buy me a coffee Build and Deploy Downloads Coverage Status npm version npm unpkg Repo Dependents

React component preview markdown text in web browser. The minimal amount of CSS to replicate the GitHub Markdown style. The current document website is converted using this react component.

Features

  • πŸŒ’ Support dark-mode/night-mode. @v4
  • πŸ™†πŸΌβ€β™‚οΈ GitHub style: The markdown content is rendered as close to the way it's rendered on GitHub as possible.
  • πŸ‹πŸΎβ€β™‚οΈ Support GFM (autolink literals, footnotes, strikethrough, tables, tasklists).
  • 🍭 Support automatic code block highlight.
  • 🐝 Support for defining styles via comment.
  • ⛳️ Support for GFM footnotes
  • ⛳️ Support for Github Alert

Quick Start

$ npm install @uiw/react-markdown-preview --save

Usage Example

Open in CodeSandbox

import React from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview';

const source = `
## MarkdownPreview

> todo: React component preview markdown text.
`;

export default function Demo() {
  return (
    <MarkdownPreview source={source} style={{ padding: 16 }} />
  )
}

Disable Header links

import React from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview';

const source = `
## MarkdownPreview

## Header 2

### Header 3
`;

export default function Demo() {
  return (
    <MarkdownPreview
      source={source}
      style={{ padding: 16 }}
      rehypeRewrite={(node, index, parent) => {
        if (node.tagName === "a" && parent && /^h(1|2|3|4|5|6)/.test(parent.tagName)) {
          parent.children = parent.children.slice(1)
        }
      }}
    />
  );
}

highlight line

syntax: ```jsx {1,4-5}

import React from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview';

const source = `
\`\`\`js {2}
function () {
  console.log('hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello')
}
\`\`\`
\`\`\`js {2}
function () {
  console.log('hello ')
}
\`\`\`
`;

export default function Demo() {
  return (
    <MarkdownPreview
      source={source}
      style={{ padding: 16 }}
      rehypeRewrite={(node, index, parent) => {
        if (node.tagName === "a" && parent && /^h(1|2|3|4|5|6)/.test(parent.tagName)) {
          parent.children = parent.children.slice(1)
        }
      }}
    />
  );
}

Show Line Numbers

syntax: ```jsx showLineNumbers {1,4-5}

import React from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview';

const source = `
\`\`\`js showLineNumbers
function () {
  console.log('hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello')
}
\`\`\`
\`\`\`js showLineNumbers {2}
function () {
  console.log('hello ')
}
\`\`\`
`;

export default function Demo() {
  return (
    <MarkdownPreview
      source={source}
      style={{ padding: 16 }}
      rehypeRewrite={(node, index, parent) => {
        if (node.tagName === "a" && parent && /^h(1|2|3|4|5|6)/.test(parent.tagName)) {
          parent.children = parent.children.slice(1)
        }
      }}
    />
  );
}

Code Highlight

import React from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview';

const source = `
\`\`\`js
function () {
  console.log('hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello')
}
\`\`\`
\`\`\`js
function () {
  console.log('hello ')
}
\`\`\`
`;

export default function Demo() {
  return (
    <MarkdownPreview source={source} style={{ padding: 16 }} />
  );
}

Remove Code Highlight

The following example can help you exclude code highlighting code from being included in the bundle. @uiw/react-markdown-preview/nohighlight component does not contain the rehype-prism-plus code highlighting package, showLineNumbers and highlight line functions will no longer work. (#586)

import React from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview/nohighlight';

const source = `
\`\`\`js
function () {
  console.log('hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello')
}
\`\`\`
\`\`\`js
function () {
  console.log('hello ')
}
\`\`\`
`;

export default function Demo() {
  return (
    <MarkdownPreview
      source={source}
      style={{ padding: 16 }}
      rehypeRewrite={(node, index, parent) => {
        if (node.tagName === "a" && parent && /^h(1|2|3|4|5|6)/.test(parent.tagName)) {
          parent.children = parent.children.slice(1)
        }
      }}
    />
  );
}

Ignore

Ignore content display via HTML comments, Shown in GitHub readme, excluded in HTML.

import React from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview';

const source = `
<!--rehype:ignore:start-->
Content ignored
<!--rehype:ignore:end-->
Some content is ignored, please check the source code
`;

export default function Demo() {
  return (
    <MarkdownPreview
      source={source}
      style={{ padding: 16 }}
      rehypeRewrite={(node, index, parent) => {
        if (node.tagName === "a" && parent && /^h(1|2|3|4|5|6)/.test(parent.tagName)) {
          parent.children = parent.children.slice(1)
        }
      }}
    />
  );
}
<!--rehype:ignore:start-->Ignored content<!--rehype:ignore:end-->

Support Custom KaTeX Preview

KaTeX is a fast, easy-to-use JavaScript library for TeX math rendering on the web, We perform math rendering through KaTeX.

npm install katex
import React from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview';
import { getCodeString } from 'rehype-rewrite';
import katex from 'katex';
import 'katex/dist/katex.css';

const source = `This is to display the 
\`\$\$\c = \\pm\\sqrt{a^2 + b^2}\$\$\`
 in one line

\`\`\`KaTeX
c = \\pm\\sqrt{a^2 + b^2}
\`\`\`
`;

export default function Demo() {
  const [value, setValue] = React.useState(source);
  return (
    <MarkdownPreview
      source={source}
      style={{ padding: 16 }}
      components={{
        code: ({ children = [], className, ...props }) => {
          if (typeof children === 'string' && /^\$\$(.*)\$\$/.test(children)) {
            const html = katex.renderToString(children.replace(/^\$\$(.*)\$\$/, '$1'), {
              throwOnError: false,
            });
            return <code dangerouslySetInnerHTML={{ __html: html }} style={{ background: 'transparent' }} />;
          }
          const code = props.node && props.node.children ? getCodeString(props.node.children) : children;
          if (
            typeof code === 'string' &&
            typeof className === 'string' &&
            /^language-katex/.test(className.toLocaleLowerCase())
          ) {
            const html = katex.renderToString(code, {
              throwOnError: false,
            });
            return <code style={{ fontSize: '150%' }} dangerouslySetInnerHTML={{ __html: html }} />;
          }
          return <code className={String(className)}>{children}</code>;
        },
      }}
    />
  );
}

Support Custom Mermaid Preview

Using mermaid to generation of diagram and flowchart from text in a similar manner as markdown

Open in CodeSandbox

import React, { useState, useRef, useEffect, Fragment, useCallback } from "react";
import MarkdownPreview from '@uiw/react-markdown-preview';
import { getCodeString } from 'rehype-rewrite';
import mermaid from "mermaid";

const randomid = () => parseInt(String(Math.random() * 1e15), 10).toString(36);
const Code = ({ inline, children = [], className, ...props }) => {
  const demoid = useRef(`dome${randomid()}`);
  const [container, setContainer] = useState(null);
  const isMermaid = className && /^language-mermaid/.test(className.toLocaleLowerCase());
  const code = props.node && props.node.children ? getCodeString(props.node.children) : children[0] || '';

  const reRender = async () => {
    if (container && isMermaid) {
      try {
        const str = await mermaid.render(demoid.current, code);
        container.innerHTML = str.svg;
      } catch (error) {
        container.innerHTML = error;
      }
    }
  }

  useEffect(() => {
    reRender()
  }, [container, isMermaid, code, demoid]);

  const refElement = useCallback((node) => {
    if (node !== null) {
      setContainer(node);
    }
  }, []);

  if (isMermaid) {
    return (
      <Fragment>
        <code id={demoid.current} style={{ display: "none" }} />
        <code ref={refElement} data-name="mermaid" />
      </Fragment>
    );
  }
  return <code>{children}</code>;
};
const source = `The following are some examples of the diagrams, charts and graphs that can be made using Mermaid and the Markdown-inspired text specific to it. 

\`\`\`mermaid
graph TD
A[Hard] -->|Text| B(Round)
B --> C{Decision}
C -->|One| D[Result 1]
C -->|Two| E[Result 2]
\`\`\`

\`\`\`mermaid
sequenceDiagram
Alice->>John: Hello John, how are you?
loop Healthcheck
    John->>John: Fight against hypochondria
end
Note right of John: Rational thoughts!
John-->>Alice: Great!
John->>Bob: How about you?
Bob-->>John: Jolly good!
\`\`\`
`;
// const source = `
// \`\`\`mermaid
// graph TD;
//     A-->B;
//     A-->C;
//     B-->D;
//     C-->D;
// \`\`\`
// `;

export default function Demo() {
  return (
    <MarkdownPreview
      source={source}
      style={{ padding: 16 }}
      components={{
        code: Code
      }}
    />
  );
}

Security

Please note markdown needs to be sanitized if you do not completely trust your authors. Otherwise, your app is vulnerable to XSS. This can be achieved by adding rehype-sanitize as a plugin.

import React from 'react';
import rehypeSanitize from "rehype-sanitize";
import MarkdownPreview from '@uiw/react-markdown-preview';

const source = `
## MarkdownPreview

**Hello world!!!** <IFRAME SRC=\"javascript:javascript:alert(window.origin);\"></IFRAME>

<!-- test --> 123

<!-- test --> 456 <!-- test -->
`;

const rehypePlugins = [rehypeSanitize];
export default function Demo() {
  return (
    <MarkdownPreview source={source} rehypePlugins={rehypePlugins} style={{ padding: 16 }} />
  )
}

Options Props

import { ReactMarkdownProps } from 'react-markdown';
import { RehypeRewriteOptions } from 'rehype-rewrite';

type MarkdownPreviewProps = {
  prefixCls?: string;
  className?: string;
  source?: string;
  disableCopy?: boolean;
  style?: React.CSSProperties;
  pluginsFilter?: (type: 'rehype' | 'remark', plugin: PluggableList) => PluggableList;
  wrapperElement?: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> & {
    'data-color-mode'?: 'light' | 'dark';
  };
  onScroll?: (e: React.UIEvent<HTMLDivElement>) => void;
  onMouseOver?: (e: React.MouseEvent<HTMLDivElement>) => void;
  rehypeRewrite?: RehypeRewriteOptions['rewrite'];
} & ReactMarkdownProps;
  • source (string, default: '')
    Markdown to parse
  • className (string?)
    Wrap the markdown in a div with this class name

This ReactMarkdownProps details. Upgrade react-markdown v9

  • children (string, default: '')
    Markdown to parse
  • className (string?)
    Wrap the markdown in a div with this class name
  • skipHtml (boolean, default: false -> true )
    Ignore HTML in Markdown completely
  • allowElement ((element, index, parent) => boolean?, optional)
    Function called to check if an element is allowed (when truthy) or not. allowedElements / disallowedElements is used first!
  • remarkPlugins (Array.<Plugin>, default: [])
    List of remark plugins to use. See the next section for examples on how to pass options
  • rehypePlugins (Array.<Plugin>, default: [])
    List of rehype plugins to use. See the next section for examples on how to pass options

Add urlTransform

The transformImageUri and transformLinkUri were removed. Having two functions is a bit much, particularly because there are more URLs you might want to change (or which might be unsafe so we make them safe). And their name and APIs were a bit weird. You can use the new urlTransform prop instead to change all your URLs.

Remove linkTarget

The linkTarget option was removed; you should likely not set targets. If you want to, use rehype-external-links.

Remove includeElementIndex

The includeElementIndex option was removed, so index is never passed to components. Write a plugin to pass index:

Show example of plugin
import {visit} from 'unist-util-visit'

function rehypePluginAddingIndex() {
  /**
   * @param {import('hast').Root} tree
   * @returns {undefined}
   */
  return function (tree) {
    visit(tree, function (node, index) {
      if (node.type === 'element' && typeof index === 'number') {
        node.properties.index = index
      }
    })
  }
}

Remove rawSourcePos

The rawSourcePos option was removed, so sourcePos is never passed to components. All components are passed node, so you can get node.position from them.

Remove sourcePos

The sourcePos option was removed, so data-sourcepos is never passed to elements. Write a plugin to pass index:

Show example of plugin
import {stringifyPosition} from 'unist-util-stringify-position'
import {visit} from 'unist-util-visit'

function rehypePluginAddingIndex() {
  /**
   * @param {import('hast').Root} tree
   * @returns {undefined}
   */
  return function (tree) {
    visit(tree, function (node) {
      if (node.type === 'element') {
        node.properties.dataSourcepos = stringifyPosition(node.position)
      }
    })
  }
}

Remove extra props passed to certain components

When overwriting components, these props are no longer passed:

  • inline on code β€” create a plugin or use pre for the block
  • level on h1, h2, h3, h4, h5, h6 β€” check node.tagName instead
  • checked on li β€” check task-list-item class or check props.children
  • index on li β€” create a plugin
  • ordered on li β€” create a plugin or check the parent
  • depth on ol, ul β€” create a plugin
  • ordered on ol, ul β€” check node.tagName instead
  • isHeader on td, th β€” check node.tagName instead
  • isHeader on tr β€” create a plugin or check children

Markdown Features

Supports for CSS Style

Use HTML comments <!--rehype:xxx--> to let Markdown support style customization.

## Title
<!--rehype:style=display: flex; height: 230px; align-items: center; justify-content: center; font-size: 38px;-->

Markdown Supports **Style**<!--rehype:style=color: red;-->

Support for GFM footnotes

Here is a simple footnote[^1]. With some additional text after it.

[^1]: My reference.

Ignore content display

# Hello World

<!--rehype:ignore:start-->Hello World<!--rehype:ignore:end-->

Good!

Output:

<h1>Hello World</h1>

<p>Good!</p>

Support for Github Alerts

import React from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview';

const source = `> 
> 
> [!NOTE]
> Useful information that users should know, even when skimming content.

> [!TIP]
> Helpful advice for doing things better or more easily.

> [!IMPORTANT]
> Key information users need to know to achieve their goal.

> [!WARNING]
> Urgent info that needs immediate user attention to avoid problems.

> [!CAUTION]
> Advises about risks or negative outcomes of certain actions.


`;

export default function Demo() {
  return (
    <MarkdownPreview source={source} style={{ padding: 16 }} />
  )
}

Support dark-mode/night-mode

By default, the dark-mode is automatically switched according to the system. If you need to switch manually, just set the data-color-mode="dark" parameter for body.

<html data-color-mode="dark">
document.documentElement.setAttribute('data-color-mode', 'dark')
document.documentElement.setAttribute('data-color-mode', 'light')

Inherit custom color variables by adding .wmde-markdown-var selector.

const Demo = () => {
  return (
    <div>
      <div className="wmde-markdown-var"> </div>
      <MarkdownPreview source="Hello World!" />
    </div>
  )
}

Set the light theme.

<MarkdownPreview
  source="Hello World!"
  wrapperElement={{
+    "data-color-mode": "light"
  }}
/>

Development

Runs the project in development mode.

# Step 1, run first,
# listen to the component compile and output the .js file
# listen for compilation output type .d.ts file
# listen to the component compile and output the .css file
npm run start
# Step 2, development mode, listen to compile preview website instance
npm run doc

Builds the app for production to the build folder.

npm run build

The build is minified and the filenames include the hashes. Your app is ready to be deployed!

Alternatives

If you need more features-rich Markdown Editor, you can use @uiwjs/react-markdown-editor

Contributors

As always, thanks to our amazing contributors!

Made with action-contributors.

License

Licensed under the MIT License.

react-markdown-preview's People

Contributors

cragonnyunt avatar gokhangunduz avatar jaywcjlove avatar lianghx-319 avatar rargames avatar renovate-bot avatar renovate[bot] avatar tjbroodryk avatar tnhu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

react-markdown-preview's Issues

Need a way to target h1-h6 for theme styling

I am trying to adjust the styling for h1-h6 using a javascript object passed it but I cannot target headers.
I am using this package with styled components. This is what I am trying now but the styling does not take. I have also tried h1-h6 as the keys and also not working.

  const [theme, setTheme] = useState(false);
  const darkTheme = {
    background: shades.MIDNIGHT_BLACK,
    color: shades.WHITE,
  };

  const lightTheme = {
    background: shades.WHITE,
    color: shades.BLACK,
    '.wmde-markdown h1': { color: shades.BLACK, },
    '.wmde-markdown h2': { color: shades.BLACK, },
    '.wmde-markdown h3': { color: shades.BLACK, },
    '.wmde-markdown h4': { color: shades.BLACK, },
    '.wmde-markdown h5': { color: shades.BLACK, },
    '.wmde-markdown h6': { color: shades.BLACK, },
  };

  const themeStyle = theme ? lightTheme : darkTheme;

  return (
    <Root>
      <StyledRow>
        <StyledSpan>Markdown Theme</StyledSpan>
        <Switch
          checkedChildren="β˜€οΈ"
          unCheckedChildren="πŸŒ™"
          onChange={() => setTheme(!theme)}
        />
      </StyledRow>
      <MarkdownPreview
        source={selectedFile.content}
        style={{ height: '100%', ...themeStyle }}
      />
    </Root>

  )


examples how to use remark plugins

hi, i'm looking for examples how to use remark plugins. The readme shows theres a prop remarkPlugins so i'm using that. Then it says See the next section for examples on how to pass options but i dont see a "next section" with an example. Specifically i want to add variables to the markdown and view the rendered values in the preview (https://github.com/mrzmmr/remark-variables) (and/or write my own simple preprocessor that does a .replaceAll on the markdown text before it is passed to the previewer.) Thanks!.

just get an warning

Warning: Using UNSAFE_componentWillReceiveProps in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.

  • Move data fetching code or side effects to componentDidUpdate.
  • If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://fb.me/react-derived-state

Please update the following components: MarkdownPreview

Vite doesn't support dynamically import from node_modules

I'm trying to use this library with Vite and that produces this error:

18:39:01 [vite] warning: 
/Users/foo/vite-test/node_modules/.vite/@uiw_react-markdown-preview.js
11080|              }
11081|              return _context.abrupt("return", Promise.all(langs.map(function(key) {
11082|                return import("prismjs/components/prism-".concat(key));
   |                              ^
11083|              })));
11084|            case 5:
The above dynamic import cannot be analyzed by vite.
See https://github.com/rollup/plugins/tree/master/packages/dynamic-import-vars#limitations for supported dynamic import formats. If this is intended to be left as-is, you can use the /* @vite-ignore */ comment inside the import() call to suppress this warning.

  Plugin: vite:import-analysis
  File: /Users/foo/vite-test/node_modules/.vite/@uiw_react-markdown-preview.js?v=59696cce

It's talking about this line:

return Promise.all(langs.map((key) => import(`prismjs/components/prism-${key}`)));

Vite (or Rollup, I'm not sure) has a feature where it rewrites "bare modules" (modules which path is not absolute nor relative) so it's imported from node_modules.

If we follow the link in the output we can see that there are some limitations to this feature:

  • Imports must start with ./ or ../.
  • Imports must end with a file extension

So it's not possible to use this library with Vite due to this single import.

Remark directive usage

Hi!
I would like to use the remark-directive plugin with the markdown previewer (just for the ::: warning syntax) but I cannot understand how to do it. Is there any useful example or something?

function MarkdownEditorPreview({ source }: MarkdownEditorPreviewProps) {
  return (
    <MarkdownEditorPreviewElement
      source={source}
      rehypeRewrite={avoidHeaderLink}
      wrapperElement={{
        "data-color-mode": "light",
      }}
      remarkPlugins={[remarkDirective]}
    />
  );
}

Thanks

Uncaught Error: require() of ES Module

Hey everybody,
importing reac-md-editor starts a dependency train to this module which I somehow cannot import into my electron app. I am on React 17.

Uncaught Error: require() of ES Module.../node_modules/@uiw/react-md-editor/node_modules/react-markdown/index.js from ../node_modules/@uiw/react-md-editor/node_modules/@uiw/react-markdown-preview/lib/index.js not supported.
Instead change the require of .../node_modules/@uiw/react-md-editor/node_modules/react-markdown/index.js in .../node_modules/@uiw/react-md-editor/node_modules/@uiw/react-markdown-preview/lib/index.js to a dynamic import() which is available in all CommonJS modules.

Remove browserslist "not dead" from package.json in v3.0.6

Hi @jaywcjlove, we are restricted to use v3.0.6 because of react version and loader issues.
But when we make a build of our CRA app we get into build error caused by borwserslist

We found that removing "not dead" from browserslist fixed the issue.
Can we remove "browserslist": {} from package.json for this specific version or you could create a patch version fix for this?

Error when upgrading to version 2.0.0

I started using your project recently and I installed the new version today, but I get this error when rendering the component.

Uncaught TypeError: combine is not a function

Screenshot from 2020-12-08 19-05-05

It is happening with version 2.0.0 and 2.1.0. So I believe the issue was probably introduced with the react-markdown update.

I tried downgraded to version 1.0.9 and it is working ok.

help me next config

how to configure next-translate & next-css. I tried to fix the picture but the error still occurs
image

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/ci.marster.yml
  • actions/checkout v4
  • actions/setup-node v4
  • peaceiris/actions-gh-pages v4
  • ncipollo/release-action v1
.github/workflows/pr.yml
  • actions/checkout v4
  • actions/setup-node v4
npm
core/package.json
  • @babel/runtime ^7.17.2
  • @uiw/copy-to-clipboard ~1.0.12
  • react-markdown ~9.0.1
  • rehype-attr ~3.0.1
  • rehype-autolink-headings ~7.1.0
  • rehype-ignore ^2.0.0
  • rehype-prism-plus 2.0.0
  • rehype-raw ^7.0.0
  • rehype-rewrite ~4.0.0
  • rehype-slug ~6.0.0
  • remark-gfm ~4.0.0
  • remark-github-blockquote-alert ^1.0.0
  • unist-util-visit ^5.0.0
  • react >=16.8.0
  • react-dom >=16.8.0
package.json
  • @testing-library/react ^14.0.0
  • @types/react ~18.2.0
  • @types/react-dom ~18.2.0
  • @types/react-test-renderer ~18.0.0
  • @kkt/ncc ^1.0.15
  • compile-less-cli ~1.9.0
  • husky ~9.0.0
  • prettier ^3.0.0
  • pretty-quick ~4.0.0
  • lerna ^8.0.0
  • react ~18.2.0
  • react-dom ~18.2.0
  • react-test-renderer ~18.2.0
  • tsbb ^4.2.4
  • node >=16.0.0
website/package.json
  • @uiw/react-markdown-preview-example ^2.1.3
  • @uiw/react-shields ^2.0.1
  • katex ^0.16.9
  • mermaid ^10.6.1
  • react ~18.2.0
  • react-dom ~18.2.0
  • rehype-sanitize ^6.0.0
  • @kkt/less-modules ^7.4.7
  • @kkt/scope-plugin-options ^7.4.7
  • kkt ^7.4.7
  • markdown-react-code-preview-loader ^2.1.8
  • prettier ^3.0.0
  • pretty-quick ^4.0.0
  • react-test-renderer ^18.2.0
  • source-map-explorer ~2.5.2
  • node ^14.13.1 || >=16.0.0

  • Check this box to trigger a request for Renovate to run again on this repository

Error [ERR_REQUIRE_ESM]: require() of ES Module

Hi, I m using Next.js v13 and I already use next-remove-imports, And I also check issue #184.
In my project, I have to render markdown in server side.

I got below Error when I import react-markdown-preview directly or dynamic import (next/dynamic) with ssr option as true.

error - Error [ERR_REQUIRE_ESM]: require() of ES Module .../node_modules/react-markdown/index.js from .../node_modules/@uiw/react-markdown-preview/lib/index.js not supported.

In my opinion, this error is caused by module between CommonJS and ES Module.
#184 and I both import out of browser which is electron, node.js, maybe they use CommonJS module System.
In node_modules/@uiw/react-markdown-preview/lib/index.js, some project was imported by CommonJS, but I Guess, they export ES module system only.

Now, I solve this problem by changing require() to (async () => await import())() in node_modules/@uiw/react-markdown-preview/lib/index.js
But I think this way is a bad idea.

Has anyone solved this problem?

Markdown.css gives error in NextJs

error - ./node_modules/@uiw/react-markdown-preview/esm/styles/markdown.css
Global CSS cannot be imported from within node_modules.
Read more: https://nextjs.org/docs/messages/css-npm
Location: node_modules/@uiw/react-markdown-preview/esm/index.js

Uncaught ReferenceError: Cannot access 'me' before initialization

Hi,

I am using this package on a VITE + React 18.2.0 setup (TS) and things seem to work fine on local.

Upon compile and push to static web app on azure, I get the following issue...

@uiw-64952b32.js:26 Uncaught ReferenceError: Cannot access 'me' before initialization
at @uiw-64952b32.js:26:10652

Which resolves to...

const ra = me.forwardRef((e,t)=>{ var {prefixCls: n="wmde-markdown wmde-markdown-color", className: r, source: i, style: u, disableCopy: l=!1, skipHtml: a=!0, onScroll: s, onMouseOver: o, pluginsFilter: c, rehypeRewrite: m, wrapperElement: d={}, warpperElement: p={}} = e , S = Dn(e, Ol) , b = me.useRef(null); Zt.useImperativeHandle(t, ()=>be({}, e, { mdp: b }), [b, e]);

Any thouhgts on what this could be, working fine on a vite dev local, but vite build and push is coming up with this.

thanks

Paul

I'm using a route to save the images on S3, but when I take a screenshot and paste it, it gives an error

I'm using OnPaste to store my save function in S3, but it's giving 404, could anyone help me solve this problem? here is the code.
`
async function onPastImage(event: React.ClipboardEvent) {
var items = event.clipboardData?.items;

if (items) {
for (var i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') !== -1) {
//image
let image = items[i].getAsFile();
if (image) {
let imageUpload: File;
imageUpload = new File([image], 'image.png');
console.log('upload', imageUpload);

      const formData = new FormData();

      console.log('const', formData);

      formData.append('image', imageUpload);

      console.log('image', formData);

      await uploadImagesService(formData);
    }
  }
}

}
}
`

<MDEditor value={doc} onPaste={onPastImage} onChange={(event) => { if (event === undefined) { return; } setDoc(event); }} />

How to use a custom container

The idea was to use Framer motion to create animated containers when loaded etc, Is there a way to replace the preview container by a customized React component? I have tried the following but doesn't seem to work:

import { motion } from "framer-motion";

<MarkdownPreview
  source={game.cached.definition?.description}
  linkTarget="_blank"
  components={{
    div({ node, className, children, ...props }) {
      return <motion.div className="MyCustomizedContainer">{children}</motion.div>;
    },
  }}
/>

Bug: Issue with `rehype-rewrite` not adhering to strict EcmaScript Module standard

With the recent bump of the rehype-rewrite module to version 2.x in the version 3.3.1 release, we are seeing an error in our Webpack 5 builds:

  ERROR in ./node_modules/rehype-rewrite/lib/index.js 1:0-28
  Module not found: Error: Can't resolve './visit' in '<path>/node_modules/rehype-rewrite/lib'
  Did you mean 'visit.js'?
  BREAKING CHANGE: The request './visit' failed to resolve only because it was resolved as fully specified
  (probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
  The extension in the request is mandatory for it to be fully specified.
  Add the extension to the request.

I reported this to the author of that library, but I'm not sure he is going to address the issue. I thought I'd let you know as well, in case there are other options to address this.

For the moment, we have pinned @uiw/react-md-editor (which is pulling this dependency in) to version 3.6.1. That was the last version of that library that referred to version 3.3.0 of this library.

In Next.js app router(server component), it can be pre-rendered.

Hi!
Recently, I migrate my blog to Next.js app router from page router. And I noticed source of preview component is pre-rendered at server-side! I find nobody saying about this, write this issue for helping who struggling for SEO(Is there any other communication channel?)

Before code was as same as Support Next.js #446 using dynamic import.

And here is my code using app router(server-component).

Code

page.tsx

// no 'use client'
// this is server-component

async function getPost(id: number): Promise<IPost> {
    const res = await (fetch(`${API_BASE_URL}/post/${id}`));
    return await res.json();
}

export default async function Page({ params }: { params: { id: string; }; }) {
  const post = await getPost(Number(params.id));
  return  <div>
        {/* title, subtitle, .... */}
        <MarkdownViewer source={post.body} />
    </div>
}

MarkdownViewer.tsx

'use client'; // <- must be added
import MarkdwonView from "@uiw/react-markdown-preview";
export default function MarkdownViewer({ source }: { source: string; }) {
    return (
        <div className="markdown-body">
            <MarkdwonView source={source} />
        </div>
    );
}

Result

before(page router)(no pre-render for source)

image

after(app router)(pre-render for source)

image

You can check in here

Question

Guess

The reason is maybe difference from dynamic import and 'use client'. When using dynamic import there is no element for rendering and redering start in browser. However, 'use client' is not meaning perfect client side rendering, It pre-render elements which next.js can pre-render. (docs)

But I don't know how next.js decide the boundary of pre-render. If anyone knows the answer about this question, please talk about that! Thanks.

RehypeMermaid plugin not working

This is the code I'm rendering with:-

import React from 'react';
import MarkdownPreview from '@uiw/react-markdown-preview';
import rehypeMermaid from 'rehype-mermaidjs';

export default function MyMarkdownPreview({ data, setData }: { data: string; setData: (data: string) => void }) {
	return (
		<MarkdownPreview
			source={data}
			wrapperElement={{
				'data-color-mode': 'light',
			}}
			rehypePlugins={[rehypeMermaid]}
			rehypeRewrite={(node, index, parent) => {
				// eslint-disable-next-line @typescript-eslint/ban-ts-comment
				// @ts-ignore
				if (node.tagName === 'a' && parent && /^h(1|2|3|4|5|6)/.test(parent.tagName as string)) {
					parent.children = parent.children.slice(1);
				}
			}}
		/>
	);
}

The md text I'm rendering with:-

```mermaid
  graph TD;
      A-->B;
      A-->C;
      B-->D;
      C-->D;
```

It just renders it like this:-
image

Way to remove anchor heading links

Is there a property to render markdown as plain elements with the insertion of github link anchor heading links as shown below
anchor

The thing that I'm referring to is the link icon near the text content of the markdown previewer.

I have a working solution already but just want to ask the dev for insight or comment on this.

 <MarkdownRenderer
              allowElement={(el) => {
                const cn = el.properties?.class as string;

                return !(cn && cn.includes('anchor'));
              }}
              source={str}
              mb="4"
            />

Thank you.

Next.JS Implementation

Hey,

Thanks for the great repo. I found that I cant use this in my next.js app because all global CSS has to be declared on the root component. if there a way to use this without the CSS? I think that should make it work.

Screen Shot 2020-10-29 at 3 10 19 PM

Please let me know if you need any more details. thank you!

It does not respect it's container div's margins/boundaries

If you notice this image, you'll see a code segment having a large single-line text going beyond its allowed margins
(uses react-markdown-preview)
image

This is not the case on the editor though, you'll see it's view doesn't overflow it's parent div's boundary and rather creates a horizontal scrollbar to accommodate it.
(uses react-md-editor)
image

Maybe show scroll on overflow or wrap on overflow feature can be implemented? Please let me know, thanks! I just noticed this issue but I've already integrated this into my project which is going into production very soon. I would really appreciate your time. Thanks!

Some emoji not supported

I've tried :sparkles: but doesn't seem to render it correctly. It renders the text. When the unicode is rendered it displays ok.

Action Required: Fix Renovate Configuration

There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.

Error type: Cannot find preset's package (github>whitesource/merge-confidence:beta)

Is it possible to remove the high severity vulnerability introduced by prismjs ?

Hi, @jaywcjlove,

Issue Description

When I build my project, I notice that a vulnerability(high severity) CVE-2021-32723 detected in package prismjs<1.24.0 is directly referenced by @uiw/[email protected].
However, @uiw/[email protected] is so popular that a large number of latest versions of active and popular downstream projects depend on it (1,289 downloads per week and about 31 downstream projects, e.g., strapi-plugin-wysiwsg-react-md-editor 1.0.3, @jbook/remote-client 1.1.11, azurev-jbook-local-client 1.0.5, @jbook/shared-components 1.1.11, azurev-jbook-cli 1.0.5, etc.).
In this case, the vulnerability CVE-2021-32723 can be propagated into these downstream projects and expose security threats to them.
As you can see, @uiw/[email protected] is introduced into the above projects via the following package dependency paths:
(1)[email protected] βž” [email protected] βž” @uiw/[email protected] βž” @uiw/[email protected] βž” [email protected]
......

I know that it's kind of you to have removed the vulnerability since @uiw/[email protected]. But, in fact, the above large amount of downstream projects cannot easily upgrade @uiw/react-markdown-preview from version 2.1.3 to (>=3.0.4):
The projects such as azurev-jbook-local-client, which introduced @uiw/[email protected], are not maintained anymore. These unmaintained packages can neither upgrade @uiw/react-markdown-preview nor be easily migrated by the large amount of affected downstream projects.

Given the large number of downstream users, is it possible to release a new patched version with the updated dependency to remove the vulnerability from package @uiw/[email protected]?

Suggested Solution

Since these inactive projects set a version constaint 2.1.* for @uiw/react-markdown-preview on the above vulnerable dependency paths, if @uiw/react-markdown-preview removes the vulnerability from 2.1.3 and releases a new patched version @uiw/[email protected], such a vulnerability patch can be automatically propagated into the downstream projects.

In @uiw/[email protected], maybe you can try to perform the following upgrade(not crossing major version):
prismjs 1.23.0 βž” 1.24.0;
Note:
[email protected](>=1.24.0) has fixed the vulnerability CVE-2021-32723.
Thank you for your attention to this issue and welcome to share other ways to resolve the issue.

Best regards,
^_^

Using markdown file

Hello, I am creating a react app and I wanted to use this module to render a markdown file. I tried doing but it doesn't seem to work:

My file directory:
image

App.js

image
image

test.md

image

Output with the console:
image

I tried doing a similar thing in the sandbox and it works:

image

Any help will be greatπŸ˜­πŸ™

test fails after importing package depending on markdown preview

Hey! Thanks for your work on this! Awesome libraries.

I have strange behaviour that I can not fix.

I have two packages: package A, and package B.

Packages A contains a component that uses a markdown-preview. All tests in this package work.
After importing the component from package A into package B, all tests in package B fail with error message:

  ● Test suite failed to run

    Jest encountered an unexpected token

    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

    By default "node_modules" folder is ignored by transformers.

    Here's what you can do:
     β€’ If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     β€’ If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
     β€’ To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     β€’ If you need a custom transformation specify a "transform" option in your config.
     β€’ If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    Details:

    /Users/Projects/___/node_modules/react-markdown/index.js:5
    export { uriTransformer } from './lib/uri-transformer.js';
    ^^^^^^

    SyntaxError: Unexpected token 'export'

      at Runtime.createScriptFromCode (../../node_modules/jest-runtime/build/index.js:1728:14)
      at Object.<anonymous> (../../node_modules/@uiw/react-markdown-preview/src/index.tsx:2:1)

Can anyone help with the error or did anyone encounter similar? The jest's configs in both of packages are the same.

Cheers.

visit is not a function

Hey everyone!
I have a problem with rendering the preview component. I do everything like it is in the demo, but still got an error:

Uncaught TypeError: (0 , unist_util_visit__WEBPACK_IMPORTED_MODULE_1__.visit) is not a function
at reservedMeta.js:9:1
at wrapped (index.js:115:1)
at next (index.js:65:1)
at done (index.js:148:1)
at then (index.js:158:1)
at wrapped (index.js:136:1)
at next (index.js:65:1)
at Object.run (index.js:36:1)
at executor (index.js:321:1)
at Function.run (index.js:312:1)
react-dom.development.js:20085 The above error occurred in the component

Could anynone help?

A tag don't work

"a Tag" works fine in react-markdown, but not in react-md-editor's preview.

image

Maybe I use this component.

import ReactMarkdownWithHtml from 'react-markdown/with-html';

Is there any way I can extend it?

I hope the problem is solved well, and thank you for making a nice app :)

tailwindcss/typography integration

I could not integrate Tailwind CSS with MarkdownPreview.

I am trying to provide a style from TailwindCSS for each heading. It works fine when add the html manually or another markdown editor, but when it is provided via MarkdownPreview, I realised MarkdownPreview style overrides what I am trying to achieve.

Is there any way of handling styles from CSS frameworks?


        <article className="prose prose-h2:text-xl prose-h2:font-medium prose-h2:text-gray-900">
          <MarkdownPreview
            source={`## h2`}
            wrapperElement={{
              "data-color-mode": "light",
              className: "prose prose-h2:text-xl prose-h2:font-medium prose-h2:text-gray-900"
            }}
            rehypeRewrite={(node, index, parent) => {
              if (node.tagName === "a" && parent && /^h(1|2|3|4|5|6)/.test(parent.tagName)) {
                parent.children = parent.children.slice(1)
              }
            }} />
        </article>

when using SyntaxHighlighter children is [object Object] instead of string

image

// react-markdown
<ReactMarkdown
  components={{
    code({ node, inline, className, children, ...props }) {
      const match = /language-(\w+)/.exec(className || "");
      return !inline && match ? (
        <SyntaxHighlighter
          language={match[1]}
          style={markdownTheme as any}
          PreTag="div"
          {...props}
        >
          {String(children).replace(/\n$/, "")} // <============
        </SyntaxHighlighter>
      ) : (
        <code
          className={className}
          {...props}
        >
          {children}
        </code>
      );
    },
  }}
>
  {markdownSource}
</ReactMarkdown>

// @uiw/react-markdown-preview
<MarkdownPreview
  source={markdownSource}
  components={{
    code({ node, inline, className, children, ...props }) {
      const match = /language-(\w+)/.exec(className || "");
      return !inline && match ? (
        <SyntaxHighlighter
          language={match[1]}
          style={markdownTheme as any}
          PreTag="div"
          {...props}
        >
          {String(children).replace(/\n$/, "")} //  <============
        </SyntaxHighlighter>
      ) : (
        <code
          className={className}
          {...props}
        >
          {children}
        </code>
      );
    },
  }}
/>

Figure with iframe tags break markdown preview

Hi, I found a problem with figure/iframe html tags. When You try to add md text right after that, it doesn't compile.
It's easy to reproduce on codesandbox.

Maybe I'm doing the wrong thing and You know a better way to embed a video in a Markdown preview?

Versions:

  • "@uiw/react-markdown-preview": "3.1.1"
    
  • "react": "17.0.2"
    
  • "react-dom": "17.0.2"
    

Some examples:

image
image

For other tags, I have to add extra "\n " and it will work...
image

Sanitize HTML content

react-markdown-preview does not sanitize HTML content before rendering. Paste below code into https://uiwjs.github.io/react-markdown-preview and you'll see an alert showing up.

<div onmouseover="alert('alpha')">
  <a href="jAva script:alert('bravo')">delta</a>
  <img src="x" onerror="alert('charlie')">
  <iframe src="javascript:alert('delta')"></iframe>
  <math>
    <mi xlink:href="data:x,<script>alert('echo')</script>"></mi>
  </math>
</div>
<script>
require('child_process').spawn('echo', ['hack!']);
</script>

Maybe https://github.com/rehypejs/rehype-sanitize should be included?

Rollback dependency rehype-prism-plus to v1.4.1

[rehypePrism, { ignoreMissing: true }],

The node.position data is lost after using the [email protected] plugin.

<MarkdownPreview
  components={{
    code:  ({ inline, node, ...props }) => {
      if (inline) {
        return <code {...props} />;
      }
      console.log('>>>>does not exist!!!!:::', node.position)
      // `node.position` does not exist
      // position:
      //   end: {line: 44, column: 35, offset: 2393}
      //   start: {line: 44, column: 30, offset: 2388}
      return (
        <div></div>
      )
    }
  }}
  className="App-markdown"
  source={MDStr.replace(/([\s\S]*)<!--dividing-->/, '')}
/>

Add support for GitHub alerts / admonitions.

Introduction

Alerts / admonitions are warning boxes which use markdown blockquotes. See the docs for more info.

Example

Markdown

> [!NOTE]  
> Highlights information that users should take into account, even when skimming.

> [!TIP]
> Optional information to help a user be more successful.

> [!IMPORTANT]  
> Crucial information necessary for users to succeed.

> [!WARNING]  
> Critical content demanding immediate user attention due to potential risks.

> [!CAUTION]
> Negative potential consequences of an action.

Rendered

Note

Highlights information that users should take into account, even when skimming.

Tip

Optional information to help a user be more successful.

Important

Crucial information necessary for users to succeed.

Warning

Critical content demanding immediate user attention due to potential risks.

Caution

Negative potential consequences of an action.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.