markdown-to-jsx 9.1.2 → 9.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +187 -594
- package/dist/native.cjs +108 -0
- package/dist/native.d.cts +351 -0
- package/dist/native.d.ts +351 -0
- package/dist/native.js +108 -0
- package/dist/native.js.map +15 -0
- package/package.json +18 -3
package/README.md
CHANGED
|
@@ -25,39 +25,28 @@ Some special features of the library:
|
|
|
25
25
|
- [Entry Points](#entry-points)
|
|
26
26
|
- [Main](#main)
|
|
27
27
|
- [React](#react)
|
|
28
|
+
- [React Native](#react-native)
|
|
28
29
|
- [HTML](#html)
|
|
29
30
|
- [Markdown](#markdown)
|
|
30
31
|
- [Library Options](#library-options)
|
|
31
|
-
- [
|
|
32
|
-
- [options.
|
|
33
|
-
- [options.wrapper](#optionswrapper)
|
|
34
|
-
- [Other useful recipes](#other-useful-recipes)
|
|
35
|
-
- [options.wrapperProps](#optionswrapperprops)
|
|
32
|
+
- [All Options](#all-options)
|
|
33
|
+
- [options.createElement](#optionscreateelement)
|
|
36
34
|
- [options.forceWrapper](#optionsforcewrapper)
|
|
37
|
-
- [options.overrides
|
|
38
|
-
- [options.overrides - Override Any HTML Tag's Representation](#optionsoverrides---override-any-html-tags-representation)
|
|
39
|
-
- [options.overrides - Rendering Arbitrary React Components](#optionsoverrides---rendering-arbitrary-react-components)
|
|
40
|
-
- [options.createElement - Custom React.createElement behavior](#optionscreateelement---custom-reactcreateelement-behavior)
|
|
41
|
-
- [options.enforceAtxHeadings](#optionsenforceatxheadings)
|
|
35
|
+
- [options.overrides](#optionsoverrides)
|
|
42
36
|
- [options.renderRule](#optionsrenderrule)
|
|
43
37
|
- [options.sanitizer](#optionssanitizer)
|
|
44
38
|
- [options.slugify](#optionsslugify)
|
|
45
|
-
- [options.
|
|
46
|
-
|
|
47
|
-
- [options.
|
|
48
|
-
- [options.tagfilter](#optionstagfilter)
|
|
39
|
+
- [options.wrapper](#optionswrapper)
|
|
40
|
+
- [Other useful recipes](#other-useful-recipes)
|
|
41
|
+
- [options.wrapperProps](#optionswrapperprops)
|
|
49
42
|
- [Syntax highlighting](#syntax-highlighting)
|
|
50
43
|
- [Handling shortcodes](#handling-shortcodes)
|
|
51
|
-
- [Getting the smallest possible bundle size](#getting-the-smallest-possible-bundle-size)
|
|
52
44
|
- [Usage with Preact](#usage-with-preact)
|
|
53
45
|
- [AST Anatomy](#ast-anatomy)
|
|
54
46
|
- [Node Types](#node-types)
|
|
55
47
|
- [Example AST Structure](#example-ast-structure)
|
|
56
48
|
- [Type Checking](#type-checking)
|
|
57
49
|
- [Gotchas](#gotchas)
|
|
58
|
-
- [Passing props to stringified React components](#passing-props-to-stringified-react-components)
|
|
59
|
-
- [Significant indentation inside arbitrary HTML](#significant-indentation-inside-arbitrary-html)
|
|
60
|
-
- [Code blocks](#code-blocks)
|
|
61
50
|
- [Changelog](#changelog)
|
|
62
51
|
- [Donate](#donate)
|
|
63
52
|
|
|
@@ -72,37 +61,25 @@ Some special features of the library:
|
|
|
72
61
|
- **`ast` option removed**: The `ast: true` option on `compiler()` has been removed. Use the new `parser()` function instead to access the AST directly.
|
|
73
62
|
|
|
74
63
|
```typescript
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const ast = compiler('# Hello world', { ast: true })
|
|
78
|
-
|
|
79
|
-
// After (v9)
|
|
80
|
-
import { parser } from 'markdown-to-jsx'
|
|
81
|
-
const ast = parser('# Hello world')
|
|
64
|
+
/** v8 */ compiler('# Hello world', { ast: true })
|
|
65
|
+
/** v9 */ parser('# Hello world')
|
|
82
66
|
```
|
|
83
67
|
|
|
84
68
|
- **`namedCodesToUnicode` option removed**: The `namedCodesToUnicode` option has been removed. All named HTML entities are now supported by default via the full entity list, so custom entity mappings are no longer needed.
|
|
85
69
|
|
|
86
70
|
```typescript
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
compiler('≤ symbol', { namedCodesToUnicode: { le: '\u2264' } })
|
|
90
|
-
|
|
91
|
-
// After (v9)
|
|
92
|
-
import { compiler } from 'markdown-to-jsx'
|
|
93
|
-
compiler('≤ symbol') // All entities supported automatically
|
|
71
|
+
/** v8 */ compiler('≤ symbol', { namedCodesToUnicode: { le: '\u2264' } })
|
|
72
|
+
/** v9 */ compiler('≤ symbol')
|
|
94
73
|
```
|
|
95
74
|
|
|
96
75
|
- **`tagfilter` enabled by default**: Dangerous HTML tags (`script`, `iframe`, `style`, `title`, `textarea`, `xmp`, `noembed`, `noframes`, `plaintext`) are now escaped by default in both HTML string output and React JSX output. Previously these tags were rendered as JSX elements in React output.
|
|
97
76
|
|
|
98
77
|
```typescript
|
|
99
|
-
|
|
100
|
-
|
|
78
|
+
/** v8 */ tags rendered as JSX elements
|
|
79
|
+
/** v9 */ tags escaped by default
|
|
80
|
+
compiler('<script>alert("xss")</script>') // <span><script></span>
|
|
101
81
|
|
|
102
|
-
|
|
103
|
-
compiler('<script>alert("xss")</script>') // Renders as <span><script></span>
|
|
104
|
-
|
|
105
|
-
// To restore old behavior:
|
|
82
|
+
/** Restore old behavior */
|
|
106
83
|
compiler('<script>alert("xss")</script>', { tagfilter: false })
|
|
107
84
|
```
|
|
108
85
|
|
|
@@ -128,33 +105,22 @@ import { compiler, astToMarkdown, parser } from 'markdown-to-jsx/markdown'
|
|
|
128
105
|
1. **Replace `compiler(..., { ast: true })` with `parser()`**:
|
|
129
106
|
|
|
130
107
|
```typescript
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const ast = compiler(markdown, { ast: true })
|
|
134
|
-
|
|
135
|
-
// After
|
|
136
|
-
import { parser } from 'markdown-to-jsx'
|
|
137
|
-
const ast = parser(markdown)
|
|
108
|
+
/** v8 */ compiler(markdown, { ast: true })
|
|
109
|
+
/** v9 */ parser(markdown)
|
|
138
110
|
```
|
|
139
111
|
|
|
140
112
|
2. **Migrate React imports to `/react` entry point** (optional but recommended):
|
|
141
113
|
|
|
142
114
|
```typescript
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
// After (recommended)
|
|
147
|
-
import Markdown, { compiler } from 'markdown-to-jsx/react'
|
|
115
|
+
/** Legacy */ import from 'markdown-to-jsx'
|
|
116
|
+
/** Recommended */ import from 'markdown-to-jsx/react'
|
|
148
117
|
```
|
|
149
118
|
|
|
150
119
|
3. **Remove `namedCodesToUnicode` option**: All named HTML entities are now supported automatically, so you can remove any custom entity mappings.
|
|
151
120
|
|
|
152
121
|
```typescript
|
|
153
|
-
|
|
154
|
-
compiler('≤ symbol'
|
|
155
|
-
|
|
156
|
-
// After
|
|
157
|
-
compiler('≤ symbol') // Works automatically
|
|
122
|
+
/** v8 */ compiler('≤ symbol', { namedCodesToUnicode: { le: '\u2264' } })
|
|
123
|
+
/** v9 */ compiler('≤ symbol')
|
|
158
124
|
```
|
|
159
125
|
|
|
160
126
|
**Note:** The main entry point (`markdown-to-jsx`) continues to work for backward compatibility, but React code there is deprecated and will be removed in a future major release. Consider migrating to `markdown-to-jsx/react` for React-specific usage.
|
|
@@ -169,21 +135,15 @@ compiler('≤ symbol') // Works automatically
|
|
|
169
135
|
- Type `ParserResult` renamed to `ASTNode` - If you were using `MarkdownToJSX.ParserResult` in your code, update to `MarkdownToJSX.ASTNode`
|
|
170
136
|
|
|
171
137
|
```typescript
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
// After
|
|
176
|
-
const nodes: MarkdownToJSX.ASTNode[] = parse(markdown)
|
|
138
|
+
/** v7 */ MarkdownToJSX.ParserResult[]
|
|
139
|
+
/** v8+ */ MarkdownToJSX.ASTNode[]
|
|
177
140
|
```
|
|
178
141
|
|
|
179
142
|
- Multiple `RuleType` enums consolidated into `RuleType.textFormatted` - If you were checking for `RuleType.textBolded`, `RuleType.textEmphasized`, `RuleType.textMarked`, or `RuleType.textStrikethroughed`, update to check for `RuleType.textFormatted` and inspect the node's boolean flags:
|
|
180
143
|
|
|
181
144
|
```typescript
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
// After
|
|
186
|
-
if (node.type === RuleType.textFormatted && node.bold) { ... }
|
|
145
|
+
/** v7 */ RuleType.textBolded
|
|
146
|
+
/** v8+ */ RuleType.textFormatted && node.bold
|
|
187
147
|
```
|
|
188
148
|
|
|
189
149
|
</details>
|
|
@@ -239,20 +199,79 @@ For React-specific usage, import from the `/react` entry point:
|
|
|
239
199
|
```tsx
|
|
240
200
|
import Markdown, { compiler, parser, astToJSX } from 'markdown-to-jsx/react'
|
|
241
201
|
|
|
242
|
-
// Use compiler for markdown → JSX
|
|
243
202
|
const jsxElement = compiler('# Hello world')
|
|
244
203
|
|
|
245
|
-
const markdown = `# Hello world`
|
|
246
|
-
|
|
247
204
|
function App() {
|
|
248
|
-
return <Markdown children=
|
|
205
|
+
return <Markdown children="# Hello world" />
|
|
249
206
|
}
|
|
250
207
|
|
|
251
|
-
|
|
208
|
+
/** Or use parser + astToJSX */
|
|
252
209
|
const ast = parser('# Hello world')
|
|
253
210
|
const jsxElement2 = astToJSX(ast)
|
|
254
211
|
```
|
|
255
212
|
|
|
213
|
+
#### React Native
|
|
214
|
+
|
|
215
|
+
For React Native usage, import from the `/native` entry point:
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
import Markdown, { compiler, parser, astToNative } from 'markdown-to-jsx/native'
|
|
219
|
+
import { View, Text, StyleSheet, Linking } from 'react-native'
|
|
220
|
+
|
|
221
|
+
const nativeElement = compiler('# Hello world', {
|
|
222
|
+
styles: {
|
|
223
|
+
heading1: { fontSize: 32, fontWeight: 'bold' },
|
|
224
|
+
paragraph: { marginVertical: 8 },
|
|
225
|
+
link: { color: 'blue', textDecorationLine: 'underline' },
|
|
226
|
+
},
|
|
227
|
+
onLinkPress: url => {
|
|
228
|
+
Linking.openURL(url)
|
|
229
|
+
},
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
const markdown = `# Hello world
|
|
233
|
+
|
|
234
|
+
This is a [link](https://example.com) with **bold** and *italic* text.
|
|
235
|
+
`
|
|
236
|
+
|
|
237
|
+
function App() {
|
|
238
|
+
return (
|
|
239
|
+
<View>
|
|
240
|
+
<Markdown
|
|
241
|
+
children={markdown}
|
|
242
|
+
options={{
|
|
243
|
+
styles: StyleSheet.create({
|
|
244
|
+
heading1: { fontSize: 32, fontWeight: 'bold' },
|
|
245
|
+
paragraph: { marginVertical: 8 },
|
|
246
|
+
link: { color: 'blue', textDecorationLine: 'underline' },
|
|
247
|
+
}),
|
|
248
|
+
onLinkPress: url => {
|
|
249
|
+
Linking.openURL(url)
|
|
250
|
+
},
|
|
251
|
+
}}
|
|
252
|
+
/>
|
|
253
|
+
</View>
|
|
254
|
+
)
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**React Native-specific options:**
|
|
259
|
+
|
|
260
|
+
- `onLinkPress?: (url: string, title?: string) => void` - Custom handler for link presses (defaults to `Linking.openURL`)
|
|
261
|
+
- `onLinkLongPress?: (url: string, title?: string) => void` - Handler for link long presses
|
|
262
|
+
- `styles?: Partial<Record<NativeStyleKey, StyleProp<ViewStyle | TextStyle | ImageStyle>>>` - Style overrides for each element type
|
|
263
|
+
- `wrapperProps?: ViewProps | TextProps` - Props for the wrapper component (defaults to `View` for block, `Text` for inline)
|
|
264
|
+
|
|
265
|
+
**HTML Tag Mapping:**
|
|
266
|
+
HTML tags are automatically mapped to React Native components:
|
|
267
|
+
|
|
268
|
+
- `<img>` → `Image` component
|
|
269
|
+
- Block elements (`<div>`, `<section>`, `<article>`, `<blockquote>`, `<ul>`, `<ol>`, `<li>`, `<table>`, etc.) → `View` component
|
|
270
|
+
- Inline elements (`<span>`, `<strong>`, `<em>`, `<a>`, etc.) → `Text` component
|
|
271
|
+
- Type 1 blocks (`<pre>`, `<script>`, `<style>`, `<textarea>`) → `View` component
|
|
272
|
+
|
|
273
|
+
**Note:** Links are underlined by default for better accessibility and discoverability. You can override this via the `styles.link` option.
|
|
274
|
+
|
|
256
275
|
#### HTML
|
|
257
276
|
|
|
258
277
|
For HTML string output (server-side rendering), import from the `/html` entry point:
|
|
@@ -260,11 +279,9 @@ For HTML string output (server-side rendering), import from the `/html` entry po
|
|
|
260
279
|
```tsx
|
|
261
280
|
import { compiler, html, parser } from 'markdown-to-jsx/html'
|
|
262
281
|
|
|
263
|
-
// Convenience function that combines parsing and HTML rendering
|
|
264
282
|
const htmlString = compiler('# Hello world')
|
|
265
|
-
// Returns: '<h1>Hello world</h1>'
|
|
266
283
|
|
|
267
|
-
|
|
284
|
+
/** Or use parser + html */
|
|
268
285
|
const ast = parser('# Hello world')
|
|
269
286
|
const htmlString2 = html(ast)
|
|
270
287
|
```
|
|
@@ -276,253 +293,58 @@ For markdown-to-markdown compilation (normalization and formatting), import from
|
|
|
276
293
|
```typescript
|
|
277
294
|
import { compiler, astToMarkdown, parser } from 'markdown-to-jsx/markdown'
|
|
278
295
|
|
|
279
|
-
// Convenience function that parses and recompiles markdown
|
|
280
296
|
const normalizedMarkdown = compiler('# Hello world\n\nExtra spaces!')
|
|
281
|
-
// Returns: '# Hello world\n\nExtra spaces!\n'
|
|
282
297
|
|
|
283
|
-
|
|
298
|
+
/** Or work with AST */
|
|
284
299
|
const ast = parser('# Hello world')
|
|
285
300
|
const normalizedMarkdown2 = astToMarkdown(ast)
|
|
286
|
-
// Returns: '# Hello world\n'
|
|
287
301
|
```
|
|
288
302
|
|
|
289
303
|
### Library Options
|
|
290
304
|
|
|
291
|
-
####
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
#
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
compiler('Hello there old chap!', { forceBlock: true })
|
|
313
|
-
|
|
314
|
-
// renders
|
|
315
|
-
<p>Hello there old chap!</p>
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
#### options.forceInline
|
|
319
|
-
|
|
320
|
-
The inverse is also available by passing `options.forceInline = true`:
|
|
321
|
-
|
|
322
|
-
```tsx
|
|
323
|
-
<Markdown options={{ forceInline: true }}># You got it babe!</Markdown>
|
|
324
|
-
|
|
325
|
-
// or
|
|
326
|
-
compiler('# You got it babe!', { forceInline: true })
|
|
327
|
-
|
|
328
|
-
// renders
|
|
329
|
-
<span># You got it babe!</span>
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
#### options.wrapper
|
|
333
|
-
|
|
334
|
-
When 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.
|
|
335
|
-
|
|
336
|
-
```tsx
|
|
337
|
-
const str = '# Heck Yes\n\nThis is great!'
|
|
338
|
-
|
|
339
|
-
<Markdown options={{ wrapper: 'article' }}>
|
|
340
|
-
{str}
|
|
341
|
-
</Markdown>;
|
|
342
|
-
|
|
343
|
-
// or
|
|
344
|
-
|
|
345
|
-
compiler(str, { wrapper: 'article' });
|
|
346
|
-
|
|
347
|
-
// renders
|
|
348
|
-
|
|
349
|
-
<article>
|
|
350
|
-
<h1>Heck Yes</h1>
|
|
351
|
-
<p>This is great!</p>
|
|
352
|
-
</article>
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
##### Other useful recipes
|
|
356
|
-
|
|
357
|
-
To get an array of children back without a wrapper, set `wrapper` to `null`. This is particularly useful when using `compiler(…)` directly.
|
|
358
|
-
|
|
359
|
-
```tsx
|
|
360
|
-
compiler('One\n\nTwo\n\nThree', { wrapper: null })
|
|
361
|
-
|
|
362
|
-
// returns
|
|
363
|
-
;[<p>One</p>, <p>Two</p>, <p>Three</p>]
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
To 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.
|
|
367
|
-
|
|
368
|
-
#### options.wrapperProps
|
|
369
|
-
|
|
370
|
-
Props to apply to the wrapper element when `wrapper` is used.
|
|
371
|
-
|
|
372
|
-
```tsx
|
|
373
|
-
<Markdown options={{
|
|
374
|
-
wrapper: 'article',
|
|
375
|
-
wrapperProps: { className: 'post', 'data-testid': 'markdown-content' }
|
|
376
|
-
}}>
|
|
377
|
-
# Hello World
|
|
378
|
-
</Markdown>
|
|
379
|
-
|
|
380
|
-
// renders
|
|
381
|
-
<article class="post" data-testid="markdown-content">
|
|
382
|
-
<h1>Hello World</h1>
|
|
383
|
-
</article>
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
#### options.forceWrapper
|
|
387
|
-
|
|
388
|
-
By 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`.
|
|
389
|
-
|
|
390
|
-
```tsx
|
|
391
|
-
// Using `forceWrapper` with a single, inline child…
|
|
392
|
-
<Markdown options={{ wrapper: 'aside', forceWrapper: true }}>
|
|
393
|
-
Mumble, mumble…
|
|
394
|
-
</Markdown>
|
|
395
|
-
|
|
396
|
-
// renders
|
|
397
|
-
|
|
398
|
-
<aside>Mumble, mumble…</aside>
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
#### options.overrides - Void particular banned tags
|
|
402
|
-
|
|
403
|
-
Pass the `options.overrides` prop to the compiler or `<Markdown>` component with an implementation that return `null` for tags you wish to exclude from the rendered output. This provides complete removal of tags from the output.
|
|
404
|
-
|
|
405
|
-
**Note**: The `tagfilter` option provides default escaping of dangerous tags (`script`, `iframe`, `style`, `title`, `textarea`, `xmp`, `noembed`, `noframes`, `plaintext`). Use `overrides` when you need to:
|
|
406
|
-
|
|
407
|
-
- Remove additional tags not covered by `tagfilter` (like `object`)
|
|
408
|
-
- Have more control over tag removal vs. escaping
|
|
409
|
-
- Disable `tagfilter` but still want to remove specific tags
|
|
410
|
-
|
|
411
|
-
For example, to void the `iframe` tag:
|
|
412
|
-
|
|
413
|
-
```tsx
|
|
414
|
-
import Markdown from 'markdown-to-jsx'
|
|
415
|
-
import React from 'react'
|
|
416
|
-
import { render } from 'react-dom'
|
|
417
|
-
|
|
418
|
-
render(
|
|
419
|
-
<Markdown options={{ overrides: { iframe: () => null } }}>
|
|
420
|
-
<iframe src="https://potentially-malicious-web-page.com/"></iframe>
|
|
421
|
-
</Markdown>,
|
|
422
|
-
document.body
|
|
423
|
-
)
|
|
424
|
-
|
|
425
|
-
// renders: ""
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
The library does not void any tags by default (except through `tagfilter` escaping), allowing you to choose the appropriate security approach for your use case.
|
|
429
|
-
|
|
430
|
-
#### options.overrides - Override Any HTML Tag's Representation
|
|
305
|
+
#### All Options
|
|
306
|
+
|
|
307
|
+
| Option | Type | Default | Description |
|
|
308
|
+
| ----------------------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
309
|
+
| `createElement` | `function` | - | Custom React.createElement behavior (React/React Native only). See [createElement](#optionscreateelement) for details. |
|
|
310
|
+
| `disableAutoLink` | `boolean` | `false` | Disable automatic conversion of bare URLs to anchor tags. |
|
|
311
|
+
| `disableParsingRawHTML` | `boolean` | `false` | Disable parsing of raw HTML into JSX. |
|
|
312
|
+
| `enforceAtxHeadings` | `boolean` | `false` | Require space between `#` and header text (GFM spec compliance). |
|
|
313
|
+
| `forceBlock` | `boolean` | `false` | Force all content to be treated as block-level. |
|
|
314
|
+
| `forceInline` | `boolean` | `false` | Force all content to be treated as inline. |
|
|
315
|
+
| `forceWrapper` | `boolean` | `false` | Force wrapper even with single child (React/React Native only). See [forceWrapper](#optionsforcewrapper) for details. |
|
|
316
|
+
| `overrides` | `object` | - | Override HTML tag rendering. See [overrides](#optionsoverrides) for details. |
|
|
317
|
+
| `preserveFrontmatter` | `boolean` | `false` | Include frontmatter in rendered output (as `<pre>` for HTML/JSX, included in markdown). Behavior varies by compiler type. |
|
|
318
|
+
| `renderRule` | `function` | - | Custom rendering for AST rules. See [renderRule](#optionsrenderrule) for details. |
|
|
319
|
+
| `sanitizer` | `function` | built-in | Custom URL sanitizer function. See [sanitizer](#optionssanitizer) for details. |
|
|
320
|
+
| `slugify` | `function` | built-in | Custom slug generation for heading IDs. See [slugify](#optionsslugify) for details. |
|
|
321
|
+
| `tagfilter` | `boolean` | `true` | Escape dangerous HTML tags (`script`, `iframe`, `style`, etc.) to prevent XSS. |
|
|
322
|
+
| `wrapper` | `string \| component \| null` | `'div'` | Wrapper element for multiple children (React/React Native only). See [wrapper](#optionswrapper) for details. |
|
|
323
|
+
| `wrapperProps` | `object` | - | Props for wrapper element (React/React Native only). See [wrapperProps](#optionswrapperprops) for details. |
|
|
324
|
+
|
|
325
|
+
#### options.createElement
|
|
431
326
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
```tsx
|
|
435
|
-
import Markdown from 'markdown-to-jsx'
|
|
436
|
-
import React from 'react'
|
|
437
|
-
import { render } from 'react-dom'
|
|
438
|
-
|
|
439
|
-
// surprise, it's a div instead!
|
|
440
|
-
const MyParagraph = ({ children, ...props }) => <div {...props}>{children}</div>
|
|
441
|
-
|
|
442
|
-
render(
|
|
443
|
-
<Markdown
|
|
444
|
-
options={{
|
|
445
|
-
overrides: {
|
|
446
|
-
h1: {
|
|
447
|
-
component: MyParagraph,
|
|
448
|
-
props: {
|
|
449
|
-
className: 'foo',
|
|
450
|
-
},
|
|
451
|
-
},
|
|
452
|
-
},
|
|
453
|
-
}}
|
|
454
|
-
>
|
|
455
|
-
# Hello world!
|
|
456
|
-
</Markdown>,
|
|
457
|
-
document.body
|
|
458
|
-
)
|
|
459
|
-
|
|
460
|
-
/*
|
|
461
|
-
renders:
|
|
462
|
-
|
|
463
|
-
<div class="foo">
|
|
464
|
-
Hello World
|
|
465
|
-
</div>
|
|
466
|
-
*/
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
If you only wish to provide a component override, a simplified syntax is available:
|
|
470
|
-
|
|
471
|
-
```js
|
|
472
|
-
{
|
|
473
|
-
overrides: {
|
|
474
|
-
h1: MyParagraph,
|
|
475
|
-
},
|
|
476
|
-
}
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
Depending on the type of element, there are some props that must be preserved to ensure the markdown is converted as intended. They are:
|
|
480
|
-
|
|
481
|
-
- `a`: `title`, `href`
|
|
482
|
-
- `img`: `title`, `alt`, `src`
|
|
483
|
-
- `input[type="checkbox"]`: `checked`, `readonly` (specifically, the one rendered by a GFM task list)
|
|
484
|
-
- `ol`: `start`
|
|
485
|
-
- `td`: `style`
|
|
486
|
-
- `th`: `style`
|
|
487
|
-
|
|
488
|
-
Any conflicts between passed `props` and the specific properties above will be resolved in favor of `markdown-to-jsx`'s code.
|
|
489
|
-
|
|
490
|
-
Some element mappings are a bit different from other libraries, in particular:
|
|
491
|
-
|
|
492
|
-
- `span`: Used for inline text.
|
|
493
|
-
- `code`: Used for inline code.
|
|
494
|
-
- `pre > code`: Code blocks are a `code` element with a `pre` as its direct ancestor.
|
|
495
|
-
|
|
496
|
-
#### options.overrides - Rendering Arbitrary React Components
|
|
497
|
-
|
|
498
|
-
One 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.
|
|
499
|
-
|
|
500
|
-
By 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:
|
|
327
|
+
Sometimes, 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):
|
|
501
328
|
|
|
502
|
-
```
|
|
329
|
+
```javascript
|
|
503
330
|
import Markdown from 'markdown-to-jsx'
|
|
504
331
|
import React from 'react'
|
|
505
332
|
import { render } from 'react-dom'
|
|
506
333
|
|
|
507
|
-
import DatePicker from './date-picker'
|
|
508
|
-
|
|
509
334
|
const md = `
|
|
510
|
-
#
|
|
511
|
-
|
|
512
|
-
The DatePicker works by supplying a date to bias towards,
|
|
513
|
-
as well as a default timezone.
|
|
514
|
-
|
|
515
|
-
<DatePicker biasTowardDateTime="2017-12-05T07:39:36.091Z" timezone="UTC+5" />
|
|
335
|
+
# Hello world
|
|
516
336
|
`
|
|
517
337
|
|
|
518
338
|
render(
|
|
519
339
|
<Markdown
|
|
520
340
|
children={md}
|
|
521
341
|
options={{
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
342
|
+
createElement(type, props, children) {
|
|
343
|
+
return (
|
|
344
|
+
<div className="parent">
|
|
345
|
+
{React.createElement(type, props, children)}
|
|
346
|
+
</div>
|
|
347
|
+
)
|
|
526
348
|
},
|
|
527
349
|
}}
|
|
528
350
|
/>,
|
|
@@ -530,129 +352,61 @@ render(
|
|
|
530
352
|
)
|
|
531
353
|
```
|
|
532
354
|
|
|
533
|
-
|
|
355
|
+
#### options.forceWrapper
|
|
534
356
|
|
|
535
|
-
|
|
357
|
+
By 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`.
|
|
536
358
|
|
|
537
359
|
```tsx
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
import DatePicker from './date-picker'
|
|
543
|
-
|
|
544
|
-
const md = `
|
|
545
|
-
# DatePicker
|
|
546
|
-
|
|
547
|
-
The DatePicker works by supplying a date to bias towards,
|
|
548
|
-
as well as a default timezone.
|
|
360
|
+
// Using `forceWrapper` with a single, inline child…
|
|
361
|
+
<Markdown options={{ wrapper: 'aside', forceWrapper: true }}>
|
|
362
|
+
Mumble, mumble…
|
|
363
|
+
</Markdown>
|
|
549
364
|
|
|
550
|
-
|
|
551
|
-
biasTowardDateTime="2017-12-05T07:39:36.091Z"
|
|
552
|
-
timezone="UTC+5"
|
|
553
|
-
startTime={1514579720511}
|
|
554
|
-
/>
|
|
555
|
-
`
|
|
365
|
+
// renders
|
|
556
366
|
|
|
557
|
-
|
|
558
|
-
<Markdown
|
|
559
|
-
children={md}
|
|
560
|
-
options={{
|
|
561
|
-
overrides: {
|
|
562
|
-
DatePicker: {
|
|
563
|
-
component: DatePicker,
|
|
564
|
-
},
|
|
565
|
-
},
|
|
566
|
-
}}
|
|
567
|
-
/>,
|
|
568
|
-
document.body
|
|
569
|
-
)
|
|
367
|
+
<aside>Mumble, mumble…</aside>
|
|
570
368
|
```
|
|
571
369
|
|
|
572
|
-
|
|
370
|
+
#### options.overrides
|
|
573
371
|
|
|
574
|
-
|
|
575
|
-
import Markdown from 'markdown-to-jsx'
|
|
576
|
-
import React from 'react'
|
|
577
|
-
import { render } from 'react-dom'
|
|
578
|
-
import withProps from 'recompose/withProps'
|
|
372
|
+
Override HTML tag rendering or render custom React components. Three use cases:
|
|
579
373
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
const DecemberDatePicker = withProps({
|
|
583
|
-
range: {
|
|
584
|
-
start: new Date('2017-12-01'),
|
|
585
|
-
end: new Date('2017-12-31'),
|
|
586
|
-
},
|
|
587
|
-
timezone: 'UTC+5',
|
|
588
|
-
})(DatePicker)
|
|
374
|
+
**1. Remove tags:** Return `null` to completely remove tags (beyond `tagfilter` escaping):
|
|
589
375
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
376
|
+
```tsx
|
|
377
|
+
<Markdown options={{ overrides: { iframe: () => null } }}>
|
|
378
|
+
<iframe src="..."></iframe>
|
|
379
|
+
</Markdown>
|
|
380
|
+
```
|
|
595
381
|
|
|
596
|
-
|
|
597
|
-
biasTowardDateTime="2017-12-05T07:39:36.091Z"
|
|
598
|
-
timezone="UTC+5"
|
|
599
|
-
startTime={1514579720511}
|
|
600
|
-
/>
|
|
382
|
+
**2. Override HTML tags:** Change component, props, or both:
|
|
601
383
|
|
|
602
|
-
|
|
384
|
+
```tsx
|
|
385
|
+
const MyParagraph = ({ children, ...props }) => <div {...props}>{children}</div>
|
|
603
386
|
|
|
604
|
-
<
|
|
605
|
-
|
|
387
|
+
<Markdown options={{ overrides: { h1: { component: MyParagraph, props: { className: 'foo' } } } }}>
|
|
388
|
+
# Hello
|
|
389
|
+
</Markdown>
|
|
606
390
|
|
|
607
|
-
|
|
608
|
-
<Markdown
|
|
609
|
-
children={md}
|
|
610
|
-
options={{
|
|
611
|
-
overrides: {
|
|
612
|
-
DatePicker,
|
|
613
|
-
DecemberDatePicker,
|
|
614
|
-
},
|
|
615
|
-
}}
|
|
616
|
-
/>,
|
|
617
|
-
document.body
|
|
618
|
-
)
|
|
391
|
+
/** Simplified */ { overrides: { h1: MyParagraph } }
|
|
619
392
|
```
|
|
620
393
|
|
|
621
|
-
|
|
394
|
+
**3. Render React components:** Use custom components in markdown:
|
|
622
395
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
```javascript
|
|
626
|
-
import Markdown from 'markdown-to-jsx'
|
|
627
|
-
import React from 'react'
|
|
628
|
-
import { render } from 'react-dom'
|
|
396
|
+
```tsx
|
|
397
|
+
import DatePicker from './date-picker'
|
|
629
398
|
|
|
630
|
-
const md =
|
|
631
|
-
# Hello world
|
|
632
|
-
`
|
|
399
|
+
const md = `<DatePicker timezone="UTC+5" startTime={1514579720511} />`
|
|
633
400
|
|
|
634
|
-
|
|
635
|
-
<Markdown
|
|
636
|
-
children={md}
|
|
637
|
-
options={{
|
|
638
|
-
createElement(type, props, children) {
|
|
639
|
-
return (
|
|
640
|
-
<div className="parent">
|
|
641
|
-
{React.createElement(type, props, children)}
|
|
642
|
-
</div>
|
|
643
|
-
)
|
|
644
|
-
},
|
|
645
|
-
}}
|
|
646
|
-
/>,
|
|
647
|
-
document.body
|
|
648
|
-
)
|
|
401
|
+
<Markdown options={{ overrides: { DatePicker } }}>{md}</Markdown>
|
|
649
402
|
```
|
|
650
403
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
Forces the compiler to have space between hash sign `#` and the header text which is explicitly stated in the most of the [markdown specs](https://github.github.com/gfm/#atx-heading).
|
|
404
|
+
**Important notes:**
|
|
654
405
|
|
|
655
|
-
|
|
406
|
+
- Props are passed as strings; parse them in your component (e.g., `JSON.parse(columns)`)
|
|
407
|
+
- JSX interpolations (`{value}`) are passed as raw strings
|
|
408
|
+
- Some props are preserved: `a` (`href`, `title`), `img` (`src`, `alt`, `title`), `input[type="checkbox"]` (`checked`, `readonly`), `ol` (`start`), `td`/`th` (`style`)
|
|
409
|
+
- Element mappings: `span` for inline text, `code` for inline code, `pre > code` for code blocks
|
|
656
410
|
|
|
657
411
|
#### options.renderRule
|
|
658
412
|
|
|
@@ -702,7 +456,7 @@ This can be overridden and replaced with a custom sanitizer if desired via `opti
|
|
|
702
456
|
// or
|
|
703
457
|
|
|
704
458
|
compiler('[foo](javascript:alert("foo"))', {
|
|
705
|
-
sanitizer:
|
|
459
|
+
sanitizer: value => value,
|
|
706
460
|
})
|
|
707
461
|
```
|
|
708
462
|
|
|
@@ -711,135 +465,51 @@ compiler('[foo](javascript:alert("foo"))', {
|
|
|
711
465
|
By default, a [lightweight deburring function](https://github.com/quantizor/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:
|
|
712
466
|
|
|
713
467
|
```tsx
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
// or
|
|
717
|
-
|
|
468
|
+
;<Markdown options={{ slugify: str => str }}># 中文</Markdown>
|
|
718
469
|
compiler('# 中文', { slugify: str => str })
|
|
719
|
-
|
|
720
|
-
// renders:
|
|
721
|
-
<h1 id="中文">中文</h1>
|
|
722
470
|
```
|
|
723
471
|
|
|
724
472
|
The original function is available as a library export called `slugify`.
|
|
725
473
|
|
|
726
|
-
#### options.
|
|
474
|
+
#### options.wrapper
|
|
727
475
|
|
|
728
|
-
|
|
476
|
+
When 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.
|
|
729
477
|
|
|
730
478
|
```tsx
|
|
731
|
-
|
|
732
|
-
The URL https://quantizor.dev will not be rendered as an anchor tag.
|
|
733
|
-
</Markdown>
|
|
734
|
-
|
|
735
|
-
// or
|
|
736
|
-
|
|
737
|
-
compiler(
|
|
738
|
-
'The URL https://quantizor.dev will not be rendered as an anchor tag.',
|
|
739
|
-
{ disableAutoLink: true }
|
|
740
|
-
)
|
|
479
|
+
const str = '# Heck Yes\n\nThis is great!'
|
|
741
480
|
|
|
742
|
-
|
|
481
|
+
<Markdown options={{ wrapper: 'article' }}>{str}</Markdown>
|
|
743
482
|
|
|
744
|
-
|
|
745
|
-
The URL https://quantizor.dev will not be rendered as an anchor tag.
|
|
746
|
-
</span>
|
|
483
|
+
compiler(str, { wrapper: 'article' })
|
|
747
484
|
```
|
|
748
485
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
By default, YAML frontmatter at the beginning of markdown documents is parsed but not rendered in the output. Set this option to `true` to include the frontmatter in the rendered output. For HTML/JSX output, frontmatter is rendered as a `<pre>` element. For markdown-to-markdown compilation, frontmatter is included in the output markdown.
|
|
752
|
-
|
|
753
|
-
| Compiler Type | Default Behavior | When `preserveFrontmatter: true` | When `preserveFrontmatter: false` |
|
|
754
|
-
| ------------------------ | --------------------------- | -------------------------------- | --------------------------------- |
|
|
755
|
-
| **React/HTML** | ❌ Don't render frontmatter | ✅ Render as `<pre>` element | ❌ Don't render frontmatter |
|
|
756
|
-
| **Markdown-to-Markdown** | ✅ Preserve frontmatter | ✅ Preserve frontmatter | ❌ Exclude frontmatter |
|
|
757
|
-
|
|
758
|
-
```tsx
|
|
759
|
-
<Markdown options={{ preserveFrontmatter: true }}>
|
|
760
|
-
{`---
|
|
761
|
-
title: My Document
|
|
762
|
-
author: John Doe
|
|
763
|
-
---
|
|
764
|
-
|
|
765
|
-
# Content
|
|
766
|
-
|
|
767
|
-
This is the main content.`
|
|
768
|
-
}
|
|
769
|
-
</Markdown>
|
|
770
|
-
|
|
771
|
-
// renders:
|
|
772
|
-
|
|
773
|
-
<div>
|
|
774
|
-
<pre>---
|
|
775
|
-
title: My Document
|
|
776
|
-
author: John Doe
|
|
777
|
-
---</pre>
|
|
778
|
-
<h1>Content</h1>
|
|
779
|
-
<p>This is the main content.</p>
|
|
780
|
-
</div>
|
|
781
|
-
```
|
|
486
|
+
##### Other useful recipes
|
|
782
487
|
|
|
783
|
-
|
|
488
|
+
To get an array of children back without a wrapper, set `wrapper` to `null`. This is particularly useful when using `compiler(…)` directly.
|
|
784
489
|
|
|
785
490
|
```tsx
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
title: My Document
|
|
790
|
-
author: John Doe
|
|
791
|
-
---
|
|
792
|
-
|
|
793
|
-
# Content`
|
|
794
|
-
|
|
795
|
-
// With preserveFrontmatter: true (default)
|
|
796
|
-
compiler(markdown, { preserveFrontmatter: true })
|
|
797
|
-
// returns: "---\ntitle: My Document\nauthor: John Doe\n---\n\n# Content"
|
|
798
|
-
|
|
799
|
-
// With preserveFrontmatter: false
|
|
800
|
-
compiler(markdown, { preserveFrontmatter: false })
|
|
801
|
-
// returns: "# Content"
|
|
491
|
+
compiler('One\n\nTwo\n\nThree', { wrapper: null })[
|
|
492
|
+
/** Returns */ ((<p>One</p>), (<p>Two</p>), (<p>Three</p>))
|
|
493
|
+
]
|
|
802
494
|
```
|
|
803
495
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
By default, raw HTML is parsed to JSX. This behavior can be disabled if desired.
|
|
807
|
-
|
|
808
|
-
```tsx
|
|
809
|
-
<Markdown options={{ disableParsingRawHTML: true }}>
|
|
810
|
-
This text has <span>html</span> in it but it won't be rendered
|
|
811
|
-
</Markdown>;
|
|
812
|
-
|
|
813
|
-
// or
|
|
814
|
-
|
|
815
|
-
compiler('This text has <span>html</span> in it but it won't be rendered', { disableParsingRawHTML: true });
|
|
816
|
-
|
|
817
|
-
// renders:
|
|
818
|
-
|
|
819
|
-
<span>This text has <span>html</span> in it but it won't be rendered</span>
|
|
820
|
-
```
|
|
496
|
+
To 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.
|
|
821
497
|
|
|
822
|
-
#### options.
|
|
498
|
+
#### options.wrapperProps
|
|
823
499
|
|
|
824
|
-
|
|
500
|
+
Props to apply to the wrapper element when `wrapper` is used.
|
|
825
501
|
|
|
826
502
|
```tsx
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
// React output: <script></script>
|
|
503
|
+
<Markdown
|
|
504
|
+
options={{
|
|
505
|
+
wrapper: 'article',
|
|
506
|
+
wrapperProps: { className: 'post', 'data-testid': 'markdown-content' },
|
|
507
|
+
}}
|
|
508
|
+
>
|
|
509
|
+
# Hello World
|
|
510
|
+
</Markdown>
|
|
836
511
|
```
|
|
837
512
|
|
|
838
|
-
**Note**: Even when `tagfilter` is disabled, other security measures remain active:
|
|
839
|
-
|
|
840
|
-
- URL sanitization preventing `javascript:` and `vbscript:` schemes in `href` and `src` attributes
|
|
841
|
-
- Protection against `data:` URLs (except safe `data:image/*` MIME types)
|
|
842
|
-
|
|
843
513
|
### Syntax highlighting
|
|
844
514
|
|
|
845
515
|
When using [fenced code blocks](https://www.markdownguide.org/extended-syntax/#syntax-highlighting) with language annotation, that language will be added to the `<code>` element as `class="lang-${language}"`. For best results, you can use `options.overrides` to provide an appropriate syntax highlighting integration like this one using `highlight.js`:
|
|
@@ -876,7 +546,7 @@ function App() {
|
|
|
876
546
|
}
|
|
877
547
|
|
|
878
548
|
function SyntaxHighlightedCode(props) {
|
|
879
|
-
const ref =
|
|
549
|
+
const ref = React.useRef<HTMLElement | null>(null)
|
|
880
550
|
|
|
881
551
|
React.useEffect(() => {
|
|
882
552
|
if (ref.current && props.className?.includes('lang-') && window.hljs) {
|
|
@@ -933,24 +603,10 @@ function Example() {
|
|
|
933
603
|
</Markdown>
|
|
934
604
|
)
|
|
935
605
|
}
|
|
936
|
-
|
|
937
|
-
// renders
|
|
938
|
-
// <span>On a beautiful summer day, all I want to do is <span>🙂</span>.</span>
|
|
939
606
|
```
|
|
940
607
|
|
|
941
608
|
When you use `options.renderRule`, any React-renderable JSX may be returned including images and GIFs. Ensure you benchmark your solution as the `text` rule is one of the hottest paths in the system!
|
|
942
609
|
|
|
943
|
-
### Getting the smallest possible bundle size
|
|
944
|
-
|
|
945
|
-
Many 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.
|
|
946
|
-
|
|
947
|
-
Here are instructions for some of the popular bundlers:
|
|
948
|
-
|
|
949
|
-
- [webpack](https://webpack.js.org/guides/production/#specify-the-environment)
|
|
950
|
-
- [browserify plugin](https://github.com/hughsk/envify)
|
|
951
|
-
- [parcel](https://parceljs.org/production.html)
|
|
952
|
-
- [fuse-box](http://fuse-box.org/plugins/replace-plugin#notes)
|
|
953
|
-
|
|
954
610
|
### Usage with Preact
|
|
955
611
|
|
|
956
612
|
Everything 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.
|
|
@@ -1122,92 +778,29 @@ if (node.type === RuleType.heading) {
|
|
|
1122
778
|
|
|
1123
779
|
### Gotchas
|
|
1124
780
|
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
Using the [`options.overrides`](#optionsoverrides---rendering-arbitrary-react-components) functionality to render React components, props are passed into the component in stringifed form. It is up to you to parse the string to make use of the data.
|
|
781
|
+
**Props are stringified:** When using `overrides` with React components, props are passed as strings. Parse them in your component:
|
|
1128
782
|
|
|
1129
783
|
```tsx
|
|
1130
|
-
const Table
|
|
1131
|
-
JSX.IntrinsicElements['table'] & {
|
|
1132
|
-
columns: string
|
|
1133
|
-
dataSource: string
|
|
1134
|
-
}
|
|
1135
|
-
> = ({ columns, dataSource, ...props }) => {
|
|
784
|
+
const Table = ({ columns, dataSource, ...props }) => {
|
|
1136
785
|
const parsedColumns = JSON.parse(columns)
|
|
1137
786
|
const parsedData = JSON.parse(dataSource)
|
|
1138
|
-
|
|
1139
|
-
return (
|
|
1140
|
-
<div {...props}>
|
|
1141
|
-
<h1>Columns</h1>
|
|
1142
|
-
{parsedColumns.map(column => (
|
|
1143
|
-
<span key={column.key}>{column.title}</span>
|
|
1144
|
-
))}
|
|
1145
|
-
|
|
1146
|
-
<h2>Data</h2>
|
|
1147
|
-
{parsedData.map(datum => (
|
|
1148
|
-
<span key={datum.key}>{datum.Month}</span>
|
|
1149
|
-
))}
|
|
1150
|
-
</div>
|
|
1151
|
-
)
|
|
787
|
+
// ... use parsed data
|
|
1152
788
|
}
|
|
1153
|
-
|
|
1154
|
-
/**
|
|
1155
|
-
* Example HTML in markdown:
|
|
1156
|
-
*
|
|
1157
|
-
* <Table
|
|
1158
|
-
* columns={[{ title: 'Month', dataIndex: 'Month', key: 'Month' }]}
|
|
1159
|
-
* dataSource={[
|
|
1160
|
-
* {
|
|
1161
|
-
* Month: '2024-09-01',
|
|
1162
|
-
* 'Forecasted Revenue': '$3,137,678.85',
|
|
1163
|
-
* 'Forecasted Expenses': '$2,036,660.28',
|
|
1164
|
-
* key: 0,
|
|
1165
|
-
* },
|
|
1166
|
-
* ]}
|
|
1167
|
-
* />
|
|
1168
|
-
*/
|
|
1169
789
|
```
|
|
1170
790
|
|
|
1171
|
-
|
|
791
|
+
**HTML indentation:** Leading whitespace in HTML blocks is auto-trimmed based on the first line's indentation to avoid markdown syntax conflicts.
|
|
1172
792
|
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
```html
|
|
1176
|
-
<div>Hey, how are you?</div>
|
|
1177
|
-
```
|
|
1178
|
-
|
|
1179
|
-
Note 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.
|
|
1180
|
-
|
|
1181
|
-
To get around this, `markdown-to-jsx` left-trims approximately as much whitespace as the first line inside the HTML block. So for example:
|
|
1182
|
-
|
|
1183
|
-
```html
|
|
1184
|
-
<div># Hello How are you?</div>
|
|
1185
|
-
```
|
|
1186
|
-
|
|
1187
|
-
The 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.
|
|
1188
|
-
|
|
1189
|
-
> 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. 🙃
|
|
1190
|
-
|
|
1191
|
-
#### Code blocks
|
|
1192
|
-
|
|
1193
|
-
⛔️
|
|
1194
|
-
|
|
1195
|
-
```md
|
|
1196
|
-
<div>
|
|
1197
|
-
var some = code();
|
|
1198
|
-
</div>
|
|
1199
|
-
```
|
|
1200
|
-
|
|
1201
|
-
✅
|
|
793
|
+
**Code in HTML:** Don't put code directly in HTML divs. Use fenced code blocks instead:
|
|
1202
794
|
|
|
1203
795
|
````md
|
|
1204
796
|
<div>
|
|
1205
797
|
```js
|
|
1206
|
-
var
|
|
1207
|
-
```
|
|
1208
|
-
</div>
|
|
798
|
+
var code = here();
|
|
1209
799
|
````
|
|
1210
800
|
|
|
801
|
+
</div>
|
|
802
|
+
```
|
|
803
|
+
|
|
1211
804
|
## Changelog
|
|
1212
805
|
|
|
1213
806
|
See [Github Releases](https://github.com/quantizor/markdown-to-jsx/releases).
|