UNPKG

17 kBMarkdownView Raw
1**markdown-to-jsx**
2
3The most lightweight, customizable React markdown component.
4
5[![npm version](https://badge.fury.io/js/markdown-to-jsx.svg)](https://badge.fury.io/js/markdown-to-jsx) <a href="https://bundlephobia.com/result?p=markdown-to-jsx" title="markdown-to-jsx latest minified+gzip size"><img src="https://badgen.net/bundlephobia/minzip/markdown-to-jsx" alt="gzip size"></a> [![build status](https://api.travis-ci.org/probablyup/markdown-to-jsx.svg)](https://travis-ci.org/probablyup/markdown-to-jsx) [![codecov](https://codecov.io/gh/probablyup/markdown-to-jsx/branch/main/graph/badge.svg)](https://codecov.io/gh/probablyup/markdown-to-jsx) [![downloads](https://img.shields.io/npm/dm/markdown-to-jsx.svg)](https://npm-stat.com/charts.html?package=markdown-to-jsx)
6
7<!-- TOC -->
8
9- [Installation](#installation)
10- [Usage](#usage)
11 - [Parsing Options](#parsing-options)
12 - [options.forceBlock](#optionsforceblock)
13 - [options.forceInline](#optionsforceinline)
14 - [options.wrapper](#optionswrapper)
15 - [Other useful recipes](#other-useful-recipes)
16 - [options.forceWrapper](#optionsforcewrapper)
17 - [options.overrides - Override Any HTML Tag's Representation](#optionsoverrides---override-any-html-tags-representation)
18 - [options.overrides - Rendering Arbitrary React Components](#optionsoverrides---rendering-arbitrary-react-components)
19 - [options.createElement - Custom React.createElement behavior](#optionscreateelement---custom-reactcreateelement-behavior)
20 - [options.slugify](#optionsslugify)
21 - [options.namedCodesToUnicode](#optionsnamedcodestounicode)
22 - [options.disableParsingRawHTML](#optionsdisableparsingrawhtml)
23 - [Syntax highlighting](#syntax-highlighting)
24 - [Getting the smallest possible bundle size](#getting-the-smallest-possible-bundle-size)
25 - [Usage with Preact](#usage-with-preact)
26- [Gotchas](#gotchas)
27 - [Significant indentation inside arbitrary HTML](#significant-indentation-inside-arbitrary-html)
28 - [Code blocks](#code-blocks)
29- [Changelog](#changelog)
30- [Donate](#donate)
31
32<!-- /TOC -->
33
34---
35
36`markdown-to-jsx` uses a heavily-modified fork of [simple-markdown](https://github.com/Khan/simple-markdown) as its parsing engine and extends it in a number of ways to make your life easier. Notably, this package offers the following additional benefits:
37
38- Arbitrary HTML is supported and parsed into the appropriate JSX representation
39 without `dangerouslySetInnerHTML`
40
41- Any HTML tags rendered by the compiler and/or `<Markdown>` component can be overridden to include additional
42 props or even a different HTML representation entirely.
43
44- GFM task list support.
45
46- Fenced code blocks with [highlight.js](https://highlightjs.org/) support.
47
48All this clocks in at around 5 kB gzipped, which is a fraction of the size of most other React markdown components.
49
50Requires React >= 0.14.
51
52## Installation
53
54Install `markdown-to-jsx` with your favorite package manager.
55
56```shell
57npm i markdown-to-jsx
58```
59
60## Usage
61
62`markdown-to-jsx` exports a React component by default for easy JSX composition:
63
64ES6-style usage\*:
65
66```jsx
67import Markdown from 'markdown-to-jsx';
68import React from 'react';
69import { render } from 'react-dom';
70
71render(<Markdown># Hello world!</Markdown>, document.body);
72
73/*
74 renders:
75
76 <h1>Hello world!</h1>
77 */
78```
79
80\* **NOTE: JSX does not natively preserve newlines in multiline text. In general, writing markdown directly in JSX is discouraged and it's a better idea to keep your content in separate .md files and require them, perhaps using webpack's [raw-loader](https://github.com/webpack-contrib/raw-loader).**
81
82### Parsing Options
83
84#### options.forceBlock
85
86By default, the compiler will try to make an intelligent guess about the content passed and wrap it in a `<div>`, `<p>`, or `<span>` as needed to satisfy the "inline"-ness of the markdown. For instance, this string would be considered "inline":
87
88```md
89Hello. _Beautiful_ day isn't it?
90```
91
92But this string would be considered "block" due to the existence of a header tag, which is a block-level HTML element:
93
94```md
95# Whaddup?
96```
97
98However, if you really want all input strings to be treated as "block" layout, simply pass `options.forceBlock = true` like this:
99
100```jsx
101<Markdown options={{ forceBlock: true }}>Hello there old chap!</Markdown>;
102
103// or
104
105compiler('Hello there old chap!', { forceBlock: true });
106
107// renders
108
109<p>Hello there old chap!</p>;
110```
111
112#### options.forceInline
113
114The inverse is also available by passing `options.forceInline = true`:
115
116```jsx
117<Markdown options={{ forceInline: true }}># You got it babe!</Markdown>;
118
119// or
120
121compiler('# You got it babe!', { forceInline: true });
122
123// renders
124
125<span># You got it babe!</span>;
126```
127
128#### options.wrapper
129
130When there are multiple children to be rendered, the compiler will wrap the output in a `div` by default. You can override this default by setting the `wrapper` option to either a string (React Element) or a component.
131
132```jsx
133const str = '# Heck Yes\n\nThis is great!'
134
135<Markdown options={{ wrapper: 'article' }}>
136 {str}
137</Markdown>;
138
139// or
140
141compiler(str, { wrapper: 'article' });
142
143// renders
144
145<article>
146 <h1>Heck Yes</h1>
147 <p>This is great!</p>
148</article>
149```
150
151##### Other useful recipes
152
153To get an array of children back without a wrapper, set `wrapper` to `null`. This is particularly useful when using `compiler(…)` directly.
154
155```jsx
156compiler('One\n\nTwo\n\nThree', { wrapper: null });
157
158// returns
159
160[
161 (<p>One</p>),
162 (<p>Two</p>),
163 (<p>Three</p>)
164]
165```
166
167To render children at the same DOM level as `<Markdown>` with no HTML wrapper, set `wrapper` to `React.Fragment`. This will still wrap your children in a React node for the purposes of rendering, but the wrapper element won't show up in the DOM.
168
169#### options.forceWrapper
170
171By default, the compiler does not wrap the rendered contents if there is only a single child. You can change this by setting `forceWrapper` to `true`. If the child is inline, it will not necessarily be wrapped in a `span`.
172
173```jsx
174// Using `forceWrapper` with a single, inline child…
175<Markdown options={{ wrapper: 'aside', forceWrapper: true }}>
176 Mumble, mumble…
177</Markdown>
178
179// renders
180
181<aside>Mumble, mumble…</aside>
182```
183
184#### options.overrides - Override Any HTML Tag's Representation
185
186Pass the `options.overrides` prop to the compiler or `<Markdown>` component to seamlessly revise the rendered representation of any HTML tag. You can choose to change the component itself, add/change props, or both.
187
188```jsx
189import Markdown from 'markdown-to-jsx';
190import React from 'react';
191import { render } from 'react-dom';
192
193// surprise, it's a div instead!
194const MyParagraph = ({ children, ...props }) => (
195 <div {...props}>{children}</div>
196);
197
198render(
199 <Markdown
200 options={{
201 overrides: {
202 h1: {
203 component: MyParagraph,
204 props: {
205 className: 'foo',
206 },
207 },
208 },
209 }}
210 >
211 # Hello world!
212 </Markdown>,
213 document.body
214);
215
216/*
217 renders:
218
219 <div class="foo">
220 Hello World
221 </div>
222 */
223```
224
225If you only wish to provide a component override, a simplified syntax is available:
226
227```js
228{
229 overrides: {
230 h1: MyParagraph,
231 },
232}
233```
234
235Depending on the type of element, there are some props that must be preserved to ensure the markdown is converted as intended. They are:
236
237- `a`: `title`, `href`
238- `img`: `title`, `alt`, `src`
239- `input[type="checkbox"]`: `checked`, `readonly` (specifically, the one rendered by a GFM task list)
240- `ol`: `start`
241- `td`: `style`
242- `th`: `style`
243
244Any conflicts between passed `props` and the specific properties above will be resolved in favor of `markdown-to-jsx`'s code.
245
246Some element mappings are a bit different from other libraries, in particular:
247
248- `span`: Used for inline text.
249- `code`: Used for inline code.
250- `pre > code`: Code blocks are a `code` element with a `pre` as its direct ancestor.
251
252#### options.overrides - Rendering Arbitrary React Components
253
254One of the most interesting use cases enabled by the HTML syntax processing in `markdown-to-jsx` is the ability to use any kind of element, even ones that aren't real HTML tags like React component classes.
255
256By adding an override for the components you plan to use in markdown documents, it's possible to dynamically render almost anything. One possible scenario could be writing documentation:
257
258```jsx
259import Markdown from 'markdown-to-jsx';
260import React from 'react';
261import { render } from 'react-dom';
262
263import DatePicker from './date-picker';
264
265const md = `
266# DatePicker
267
268The DatePicker works by supplying a date to bias towards,
269as well as a default timezone.
270
271<DatePicker biasTowardDateTime="2017-12-05T07:39:36.091Z" timezone="UTC+5" />
272`;
273
274render(
275 <Markdown
276 children={md}
277 options={{
278 overrides: {
279 DatePicker: {
280 component: DatePicker,
281 },
282 },
283 }}
284 />,
285 document.body
286);
287```
288
289`markdown-to-jsx` also handles JSX interpolation syntax, but in a minimal way to not introduce a potential attack vector. Interpolations are sent to the component as their raw string, which the consumer can then `eval()` or process as desired to their security needs.
290
291In the following case, `DatePicker` could simply run `parseInt()` on the passed `startTime` for example:
292
293```jsx
294import Markdown from 'markdown-to-jsx';
295import React from 'react';
296import { render } from 'react-dom';
297
298import DatePicker from './date-picker';
299
300const md = `
301# DatePicker
302
303The DatePicker works by supplying a date to bias towards,
304as well as a default timezone.
305
306<DatePicker
307 biasTowardDateTime="2017-12-05T07:39:36.091Z"
308 timezone="UTC+5"
309 startTime={1514579720511}
310/>
311`;
312
313render(
314 <Markdown
315 children={md}
316 options={{
317 overrides: {
318 DatePicker: {
319 component: DatePicker,
320 },
321 },
322 }}
323 />,
324 document.body
325);
326```
327
328Another possibility is to use something like [recompose's `withProps()` HOC](https://github.com/acdlite/recompose/blob/main/docs/API.md#withprops) to create various pregenerated scenarios and then reference them by name in the markdown:
329
330```jsx
331import Markdown from 'markdown-to-jsx';
332import React from 'react';
333import { render } from 'react-dom';
334import withProps from 'recompose/withProps';
335
336import DatePicker from './date-picker';
337
338const DecemberDatePicker = withProps({
339 range: {
340 start: new Date('2017-12-01'),
341 end: new Date('2017-12-31'),
342 },
343 timezone: 'UTC+5',
344})(DatePicker);
345
346const md = `
347# DatePicker
348
349The DatePicker works by supplying a date to bias towards,
350as well as a default timezone.
351
352<DatePicker
353 biasTowardDateTime="2017-12-05T07:39:36.091Z"
354 timezone="UTC+5"
355 startTime={1514579720511}
356/>
357
358Here's an example of a DatePicker pre-set to only the month of December:
359
360<DecemberDatePicker />
361`;
362
363render(
364 <Markdown
365 children={md}
366 options={{
367 overrides: {
368 DatePicker,
369 DecemberDatePicker,
370 },
371 }}
372 />,
373 document.body
374);
375```
376
377#### options.createElement - Custom React.createElement behavior
378
379Sometimes, you might want to override the `React.createElement` default behavior to hook into the rendering process before the JSX gets rendered. This might be useful to add extra children or modify some props based on runtime conditions. The function mirrors the `React.createElement` function, so the params are [`type, [props], [...children]`](https://reactjs.org/docs/react-api.html#createelement):
380
381```javascript
382import Markdown from 'markdown-to-jsx';
383import React from 'react';
384import { render } from 'react-dom';
385
386const md = `
387# Hello world
388`;
389
390render(
391 <Markdown
392 children={md}
393 options={{
394 createElement(type, props, children) {
395 return (
396 <div className="parent">
397 {React.createElement(type, props, children)}
398 </div>
399 );
400 },
401 }}
402 />,
403 document.body
404);
405```
406
407#### options.slugify
408
409By default, a [lightweight deburring function](https://github.com/probablyup/markdown-to-jsx/blob/bc2f57412332dc670f066320c0f38d0252e0f057/index.js#L261-L275) is used to generate an HTML id from headings. You can override this by passing a function to `options.slugify`. This is helpful when you are using non-alphanumeric characters (e.g. Chinese or Japanese characters) in headings. For example:
410
411```jsx
412<Markdown options={{ slugify: str => str }}># 中文</Markdown>;
413
414// or
415
416compiler('# 中文', { slugify: str => str });
417
418// renders:
419
420<h1 id="中文">中文</h1>
421```
422
423#### options.namedCodesToUnicode
424
425By default only a couple of named html codes are converted to unicode characters:
426
427* `&` (`&amp;`)
428* `'` (`&apos;`)
429* `>` (`&gt;`)
430* `<` (`&lt;`)
431* ` ` (`&nbsp;`)
432* `"` (`&quot;`)
433
434Some projects require to extend this map of named codes and unicode characters. To customize this list with additional html codes pass the option namedCodesToUnicode as object with the code names needed as in the example below:
435
436```jsx
437<Markdown options={{ namedCodesToUnicode: {
438 le: '\u2264',
439 ge: '\u2265',
440} }}>This text is &le; than this text.</Markdown>;
441
442// or
443
444compiler('This text is &le; than this text.', namedCodesToUnicode: {
445 le: '\u2264',
446 ge: '\u2265',
447});
448
449// renders:
450
451<p>This text is ≤ than this text.</p>
452```
453
454#### options.disableParsingRawHTML
455
456By default, raw HTML is parsed to JSX. This behavior can be disabled with this option.
457
458```jsx
459<Markdown options={{ disableParsingRawHTML: true }}>
460 This text has <span>html</span> in it but it won't be rendered
461</Markdown>;
462
463// or
464
465compiler('This text has <span>html</span> in it but it won't be rendered', { disableParsingRawHTML: true });
466
467// renders:
468
469<span>This text has &lt;span&gt;html&lt;/span&gt; in it but it won't be rendered</span>
470```
471
472### Syntax highlighting
473
474Some syntax highlighters require you to specify the language. The language of the code fence is
475forwarded in the className prop of the element used for `<code>`:
476
477```jsx
478const Code = ({className, children}) => {
479 const language = className.replace("lang-", "");
480
481 return (
482 <SyntaxHighlighter language={language}>
483 <code>{children}</code>
484 </SyntaxHighlighter>
485 );
486}
487```
488### Getting the smallest possible bundle size
489
490Many development conveniences are placed behind `process.env.NODE_ENV !== "production"` conditionals. When bundling your app, it's a good idea to replace these code snippets such that a minifier (like uglify) can sweep them away and leave a smaller overall bundle.
491
492Here are instructions for some of the popular bundlers:
493
494- [webpack](https://webpack.js.org/guides/production/#specify-the-environment)
495- [browserify plugin](https://github.com/hughsk/envify)
496- [parcel](https://parceljs.org/production.html)
497- [fuse-box](http://fuse-box.org/plugins/replace-plugin#notes)
498
499### Usage with Preact
500
501Everything will work just fine! Simply [Alias `react` to `preact/compat`](https://preactjs.com/guide/v10/switching-to-preact#setting-up-compat) like you probably already are doing.
502
503## Gotchas
504
505### Significant indentation inside arbitrary HTML
506
507People usually write HTML like this:
508
509```html
510<div>
511 Hey, how are you?
512</div>
513```
514
515Note the leading spaces before the inner content. This sort of thing unfortunately clashes with existing markdown syntaxes since 4 spaces === a code block and other similar collisions.
516
517To get around this, `markdown-to-jsx` left-trims approximately as much whitespace as the first line inside the HTML block. So for example:
518
519```html
520<div>
521 # Hello
522
523 How are you?
524</div>
525```
526
527The two leading spaces in front of "# Hello" would be left-trimmed from all lines inside the HTML block. In the event that there are varying amounts of indentation, only the amount of the first line is trimmed.
528
529> NOTE! These syntaxes work just fine when you aren't writing arbitrary HTML wrappers inside your markdown. This is very much an edge case of an edge case. 🙃
530
531#### Code blocks
532
533⛔️
534
535```md
536<div>
537 var some = code();
538</div>
539```
540
541
542
543```md
544<div>
545```js
546var some = code();
547```
548</div>
549```
550
551## Using The Compiler Directly
552
553If desired, the compiler function is a "named" export on the `markdown-to-jsx` module:
554
555```jsx
556import { compiler } from 'markdown-to-jsx';
557import React from 'react';
558import { render } from 'react-dom';
559
560render(compiler('# Hello world!'), document.body);
561
562/*
563 renders:
564
565 <h1>Hello world!</h1>
566 */
567```
568
569It accepts the following arguments:
570
571```js
572compiler(markdown: string, options: object?)
573```
574
575## Changelog
576
577See [Github Releases](https://github.com/probablyup/markdown-to-jsx/releases).
578
579## Donate
580
581Like this library? It's developed entirely on a volunteer basis; chip in a few bucks if you can at my [Patreon](https://www.patreon.com/bePatron?u=27436864).
582
583MIT