markdown-to-jsx 9.1.2 → 9.3.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 +237 -571
- package/dist/html.cjs +97 -96
- package/dist/html.d.cts +4 -2
- package/dist/html.d.ts +4 -2
- package/dist/html.js +97 -96
- package/dist/html.js.map +7 -7
- package/dist/index.cjs +85 -85
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +83 -83
- package/dist/index.js.map +7 -7
- package/dist/markdown.cjs +93 -93
- package/dist/markdown.js +93 -93
- package/dist/markdown.js.map +6 -6
- package/dist/native.cjs +108 -0
- package/dist/native.d.cts +352 -0
- package/dist/native.d.ts +352 -0
- package/dist/native.js +108 -0
- package/dist/native.js.map +15 -0
- package/dist/react.cjs +83 -83
- package/dist/react.d.cts +4 -8
- package/dist/react.d.ts +4 -8
- package/dist/react.js +85 -85
- package/dist/react.js.map +7 -7
- package/dist/solid.cjs +108 -0
- package/dist/solid.d.cts +373 -0
- package/dist/solid.d.ts +373 -0
- package/dist/solid.js +108 -0
- package/dist/solid.js.map +15 -0
- package/dist/vue.cjs +108 -0
- package/dist/vue.d.cts +373 -0
- package/dist/vue.d.ts +373 -0
- package/dist/vue.js +108 -0
- package/dist/vue.js.map +15 -0
- package/package.json +59 -5
package/README.md
CHANGED
|
@@ -25,39 +25,30 @@ 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)
|
|
29
|
+
- [SolidJS](#solidjs)
|
|
30
|
+
- [Vue.js](#vuejs)
|
|
28
31
|
- [HTML](#html)
|
|
29
32
|
- [Markdown](#markdown)
|
|
30
33
|
- [Library Options](#library-options)
|
|
31
|
-
- [
|
|
32
|
-
- [options.
|
|
33
|
-
- [options.wrapper](#optionswrapper)
|
|
34
|
-
- [Other useful recipes](#other-useful-recipes)
|
|
35
|
-
- [options.wrapperProps](#optionswrapperprops)
|
|
34
|
+
- [All Options](#all-options)
|
|
35
|
+
- [options.createElement](#optionscreateelement)
|
|
36
36
|
- [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)
|
|
37
|
+
- [options.overrides](#optionsoverrides)
|
|
42
38
|
- [options.renderRule](#optionsrenderrule)
|
|
43
39
|
- [options.sanitizer](#optionssanitizer)
|
|
44
40
|
- [options.slugify](#optionsslugify)
|
|
45
|
-
- [options.
|
|
46
|
-
|
|
47
|
-
- [options.
|
|
48
|
-
- [options.tagfilter](#optionstagfilter)
|
|
41
|
+
- [options.wrapper](#optionswrapper)
|
|
42
|
+
- [Other useful recipes](#other-useful-recipes)
|
|
43
|
+
- [options.wrapperProps](#optionswrapperprops)
|
|
49
44
|
- [Syntax highlighting](#syntax-highlighting)
|
|
50
45
|
- [Handling shortcodes](#handling-shortcodes)
|
|
51
|
-
- [Getting the smallest possible bundle size](#getting-the-smallest-possible-bundle-size)
|
|
52
46
|
- [Usage with Preact](#usage-with-preact)
|
|
53
47
|
- [AST Anatomy](#ast-anatomy)
|
|
54
48
|
- [Node Types](#node-types)
|
|
55
49
|
- [Example AST Structure](#example-ast-structure)
|
|
56
50
|
- [Type Checking](#type-checking)
|
|
57
51
|
- [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
52
|
- [Changelog](#changelog)
|
|
62
53
|
- [Donate](#donate)
|
|
63
54
|
|
|
@@ -72,37 +63,25 @@ Some special features of the library:
|
|
|
72
63
|
- **`ast` option removed**: The `ast: true` option on `compiler()` has been removed. Use the new `parser()` function instead to access the AST directly.
|
|
73
64
|
|
|
74
65
|
```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')
|
|
66
|
+
/** v8 */ compiler('# Hello world', { ast: true })
|
|
67
|
+
/** v9 */ parser('# Hello world')
|
|
82
68
|
```
|
|
83
69
|
|
|
84
70
|
- **`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
71
|
|
|
86
72
|
```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
|
|
73
|
+
/** v8 */ compiler('≤ symbol', { namedCodesToUnicode: { le: '\u2264' } })
|
|
74
|
+
/** v9 */ compiler('≤ symbol')
|
|
94
75
|
```
|
|
95
76
|
|
|
96
77
|
- **`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
78
|
|
|
98
79
|
```typescript
|
|
99
|
-
|
|
100
|
-
|
|
80
|
+
/** v8 */ tags rendered as JSX elements
|
|
81
|
+
/** v9 */ tags escaped by default
|
|
82
|
+
compiler('<script>alert("xss")</script>') // <span><script></span>
|
|
101
83
|
|
|
102
|
-
|
|
103
|
-
compiler('<script>alert("xss")</script>') // Renders as <span><script></span>
|
|
104
|
-
|
|
105
|
-
// To restore old behavior:
|
|
84
|
+
/** Restore old behavior */
|
|
106
85
|
compiler('<script>alert("xss")</script>', { tagfilter: false })
|
|
107
86
|
```
|
|
108
87
|
|
|
@@ -128,33 +107,22 @@ import { compiler, astToMarkdown, parser } from 'markdown-to-jsx/markdown'
|
|
|
128
107
|
1. **Replace `compiler(..., { ast: true })` with `parser()`**:
|
|
129
108
|
|
|
130
109
|
```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)
|
|
110
|
+
/** v8 */ compiler(markdown, { ast: true })
|
|
111
|
+
/** v9 */ parser(markdown)
|
|
138
112
|
```
|
|
139
113
|
|
|
140
114
|
2. **Migrate React imports to `/react` entry point** (optional but recommended):
|
|
141
115
|
|
|
142
116
|
```typescript
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
// After (recommended)
|
|
147
|
-
import Markdown, { compiler } from 'markdown-to-jsx/react'
|
|
117
|
+
/** Legacy */ import from 'markdown-to-jsx'
|
|
118
|
+
/** Recommended */ import from 'markdown-to-jsx/react'
|
|
148
119
|
```
|
|
149
120
|
|
|
150
121
|
3. **Remove `namedCodesToUnicode` option**: All named HTML entities are now supported automatically, so you can remove any custom entity mappings.
|
|
151
122
|
|
|
152
123
|
```typescript
|
|
153
|
-
|
|
154
|
-
compiler('≤ symbol'
|
|
155
|
-
|
|
156
|
-
// After
|
|
157
|
-
compiler('≤ symbol') // Works automatically
|
|
124
|
+
/** v8 */ compiler('≤ symbol', { namedCodesToUnicode: { le: '\u2264' } })
|
|
125
|
+
/** v9 */ compiler('≤ symbol')
|
|
158
126
|
```
|
|
159
127
|
|
|
160
128
|
**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 +137,15 @@ compiler('≤ symbol') // Works automatically
|
|
|
169
137
|
- Type `ParserResult` renamed to `ASTNode` - If you were using `MarkdownToJSX.ParserResult` in your code, update to `MarkdownToJSX.ASTNode`
|
|
170
138
|
|
|
171
139
|
```typescript
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
// After
|
|
176
|
-
const nodes: MarkdownToJSX.ASTNode[] = parse(markdown)
|
|
140
|
+
/** v7 */ MarkdownToJSX.ParserResult[]
|
|
141
|
+
/** v8+ */ MarkdownToJSX.ASTNode[]
|
|
177
142
|
```
|
|
178
143
|
|
|
179
144
|
- 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
145
|
|
|
181
146
|
```typescript
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
// After
|
|
186
|
-
if (node.type === RuleType.textFormatted && node.bold) { ... }
|
|
147
|
+
/** v7 */ RuleType.textBolded
|
|
148
|
+
/** v8+ */ RuleType.textFormatted && node.bold
|
|
187
149
|
```
|
|
188
150
|
|
|
189
151
|
</details>
|
|
@@ -239,290 +201,224 @@ For React-specific usage, import from the `/react` entry point:
|
|
|
239
201
|
```tsx
|
|
240
202
|
import Markdown, { compiler, parser, astToJSX } from 'markdown-to-jsx/react'
|
|
241
203
|
|
|
242
|
-
// Use compiler for markdown → JSX
|
|
243
204
|
const jsxElement = compiler('# Hello world')
|
|
244
205
|
|
|
245
|
-
const markdown = `# Hello world`
|
|
246
|
-
|
|
247
206
|
function App() {
|
|
248
|
-
return <Markdown children=
|
|
207
|
+
return <Markdown children="# Hello world" />
|
|
249
208
|
}
|
|
250
209
|
|
|
251
|
-
|
|
210
|
+
/** Or use parser + astToJSX */
|
|
252
211
|
const ast = parser('# Hello world')
|
|
253
212
|
const jsxElement2 = astToJSX(ast)
|
|
254
213
|
```
|
|
255
214
|
|
|
256
|
-
####
|
|
215
|
+
#### React Native
|
|
257
216
|
|
|
258
|
-
For
|
|
217
|
+
For React Native usage, import from the `/native` entry point:
|
|
259
218
|
|
|
260
219
|
```tsx
|
|
261
|
-
import { compiler,
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
For markdown-to-markdown compilation (normalization and formatting), import from the `/markdown` entry point:
|
|
275
|
-
|
|
276
|
-
```typescript
|
|
277
|
-
import { compiler, astToMarkdown, parser } from 'markdown-to-jsx/markdown'
|
|
278
|
-
|
|
279
|
-
// Convenience function that parses and recompiles markdown
|
|
280
|
-
const normalizedMarkdown = compiler('# Hello world\n\nExtra spaces!')
|
|
281
|
-
// Returns: '# Hello world\n\nExtra spaces!\n'
|
|
282
|
-
|
|
283
|
-
// Or work with AST directly
|
|
284
|
-
const ast = parser('# Hello world')
|
|
285
|
-
const normalizedMarkdown2 = astToMarkdown(ast)
|
|
286
|
-
// Returns: '# Hello world\n'
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
### Library Options
|
|
290
|
-
|
|
291
|
-
#### options.forceBlock
|
|
292
|
-
|
|
293
|
-
By 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":
|
|
220
|
+
import Markdown, { compiler, parser, astToNative } from 'markdown-to-jsx/native'
|
|
221
|
+
import { View, Text, StyleSheet, Linking } from 'react-native'
|
|
222
|
+
|
|
223
|
+
const nativeElement = compiler('# Hello world', {
|
|
224
|
+
styles: {
|
|
225
|
+
heading1: { fontSize: 32, fontWeight: 'bold' },
|
|
226
|
+
paragraph: { marginVertical: 8 },
|
|
227
|
+
link: { color: 'blue', textDecorationLine: 'underline' },
|
|
228
|
+
},
|
|
229
|
+
onLinkPress: url => {
|
|
230
|
+
Linking.openURL(url)
|
|
231
|
+
},
|
|
232
|
+
})
|
|
294
233
|
|
|
295
|
-
|
|
296
|
-
Hello. _Beautiful_ day isn't it?
|
|
297
|
-
```
|
|
234
|
+
const markdown = `# Hello world
|
|
298
235
|
|
|
299
|
-
|
|
236
|
+
This is a [link](https://example.com) with **bold** and *italic* text.
|
|
237
|
+
`
|
|
300
238
|
|
|
301
|
-
|
|
302
|
-
|
|
239
|
+
function App() {
|
|
240
|
+
return (
|
|
241
|
+
<View>
|
|
242
|
+
<Markdown
|
|
243
|
+
children={markdown}
|
|
244
|
+
options={{
|
|
245
|
+
styles: StyleSheet.create({
|
|
246
|
+
heading1: { fontSize: 32, fontWeight: 'bold' },
|
|
247
|
+
paragraph: { marginVertical: 8 },
|
|
248
|
+
link: { color: 'blue', textDecorationLine: 'underline' },
|
|
249
|
+
}),
|
|
250
|
+
onLinkPress: url => {
|
|
251
|
+
Linking.openURL(url)
|
|
252
|
+
},
|
|
253
|
+
}}
|
|
254
|
+
/>
|
|
255
|
+
</View>
|
|
256
|
+
)
|
|
257
|
+
}
|
|
303
258
|
```
|
|
304
259
|
|
|
305
|
-
|
|
260
|
+
**React Native-specific options:**
|
|
306
261
|
|
|
307
|
-
|
|
308
|
-
|
|
262
|
+
- `onLinkPress?: (url: string, title?: string) => void` - Custom handler for link presses (defaults to `Linking.openURL`)
|
|
263
|
+
- `onLinkLongPress?: (url: string, title?: string) => void` - Handler for link long presses
|
|
264
|
+
- `styles?: Partial<Record<NativeStyleKey, StyleProp<ViewStyle | TextStyle | ImageStyle>>>` - Style overrides for each element type
|
|
265
|
+
- `wrapperProps?: ViewProps | TextProps` - Props for the wrapper component (defaults to `View` for block, `Text` for inline)
|
|
309
266
|
|
|
310
|
-
|
|
267
|
+
**HTML Tag Mapping:**
|
|
268
|
+
HTML tags are automatically mapped to React Native components:
|
|
311
269
|
|
|
312
|
-
|
|
270
|
+
- `<img>` → `Image` component
|
|
271
|
+
- Block elements (`<div>`, `<section>`, `<article>`, `<blockquote>`, `<ul>`, `<ol>`, `<li>`, `<table>`, etc.) → `View` component
|
|
272
|
+
- Inline elements (`<span>`, `<strong>`, `<em>`, `<a>`, etc.) → `Text` component
|
|
273
|
+
- Type 1 blocks (`<pre>`, `<script>`, `<style>`, `<textarea>`) → `View` component
|
|
313
274
|
|
|
314
|
-
|
|
315
|
-
<p>Hello there old chap!</p>
|
|
316
|
-
```
|
|
275
|
+
**Note:** Links are underlined by default for better accessibility and discoverability. You can override this via the `styles.link` option.
|
|
317
276
|
|
|
318
|
-
####
|
|
277
|
+
#### SolidJS
|
|
319
278
|
|
|
320
|
-
|
|
279
|
+
For SolidJS usage, import from the `/solid` entry point:
|
|
321
280
|
|
|
322
281
|
```tsx
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
282
|
+
import Markdown, {
|
|
283
|
+
compiler,
|
|
284
|
+
parser,
|
|
285
|
+
astToJSX,
|
|
286
|
+
MarkdownProvider,
|
|
287
|
+
} from 'markdown-to-jsx/solid'
|
|
288
|
+
import { createSignal } from 'solid-js'
|
|
327
289
|
|
|
328
|
-
//
|
|
329
|
-
|
|
330
|
-
```
|
|
290
|
+
// Static content
|
|
291
|
+
const solidElement = compiler('# Hello world')
|
|
331
292
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
|
293
|
+
function App() {
|
|
294
|
+
return <Markdown children="# Hello world" />
|
|
295
|
+
}
|
|
344
296
|
|
|
345
|
-
|
|
297
|
+
// Reactive content (automatically updates when content changes)
|
|
298
|
+
function ReactiveApp() {
|
|
299
|
+
const [content, setContent] = createSignal('# Hello world')
|
|
300
|
+
return <Markdown>{content}</Markdown>
|
|
301
|
+
}
|
|
346
302
|
|
|
347
|
-
//
|
|
303
|
+
// Or use parser + astToJSX
|
|
304
|
+
const ast = parser('# Hello world')
|
|
305
|
+
const solidElement2 = astToJSX(ast)
|
|
348
306
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
307
|
+
// Use context for default options
|
|
308
|
+
function AppWithContext() {
|
|
309
|
+
return (
|
|
310
|
+
<MarkdownProvider options={{ sanitizer: customSanitizer }}>
|
|
311
|
+
<Markdown># Content</Markdown>
|
|
312
|
+
</MarkdownProvider>
|
|
313
|
+
)
|
|
314
|
+
}
|
|
353
315
|
```
|
|
354
316
|
|
|
355
|
-
|
|
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
|
-
```
|
|
317
|
+
**SolidJS-specific features:**
|
|
365
318
|
|
|
366
|
-
|
|
319
|
+
- **Reactive content**: The `Markdown` component accepts signals/accessors for automatic updates when markdown content changes
|
|
320
|
+
- **Memoization**: AST parsing is automatically memoized for optimal performance
|
|
321
|
+
- **Context API**: Use `MarkdownProvider` to provide default options and avoid prop drilling
|
|
367
322
|
|
|
368
|
-
####
|
|
323
|
+
#### Vue.js
|
|
369
324
|
|
|
370
|
-
|
|
325
|
+
For Vue.js 3 usage, import from the `/vue` entry point:
|
|
371
326
|
|
|
372
327
|
```tsx
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
wrapperProps: { className: 'post', 'data-testid': 'markdown-content' }
|
|
376
|
-
}}>
|
|
377
|
-
# Hello World
|
|
378
|
-
</Markdown>
|
|
328
|
+
import Markdown, { compiler, parser, astToJSX } from 'markdown-to-jsx/vue'
|
|
329
|
+
import { h } from 'vue'
|
|
379
330
|
|
|
380
|
-
//
|
|
381
|
-
|
|
382
|
-
<h1>Hello World</h1>
|
|
383
|
-
</article>
|
|
384
|
-
```
|
|
331
|
+
// Using compiler
|
|
332
|
+
const vnode = compiler('# Hello world')
|
|
385
333
|
|
|
386
|
-
|
|
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`.
|
|
334
|
+
// Using component
|
|
335
|
+
<Markdown children="# Hello world" />
|
|
389
336
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
Mumble, mumble…
|
|
394
|
-
</Markdown>
|
|
395
|
-
|
|
396
|
-
// renders
|
|
397
|
-
|
|
398
|
-
<aside>Mumble, mumble…</aside>
|
|
337
|
+
// Or use parser + astToJSX
|
|
338
|
+
const ast = parser('# Hello world')
|
|
339
|
+
const vnode2 = astToJSX(ast)
|
|
399
340
|
```
|
|
400
341
|
|
|
401
|
-
|
|
342
|
+
**Vue.js-specific features:**
|
|
402
343
|
|
|
403
|
-
|
|
344
|
+
- **Vue 3 support**: Uses Vue 3's `h()` render function API
|
|
345
|
+
- **JSX support**: Works with Vue 3 JSX via `@vue/babel-plugin-jsx` or `@vitejs/plugin-vue-jsx`
|
|
346
|
+
- **HTML attributes**: Uses standard HTML attributes (`class` instead of `className`)
|
|
347
|
+
- **Component overrides**: Support for both Options API and Composition API componen
|
|
404
348
|
|
|
405
|
-
|
|
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
|
|
349
|
+
#### HTML
|
|
410
350
|
|
|
411
|
-
For
|
|
351
|
+
For HTML string output (server-side rendering), import from the `/html` entry point:
|
|
412
352
|
|
|
413
353
|
```tsx
|
|
414
|
-
import
|
|
415
|
-
import React from 'react'
|
|
416
|
-
import { render } from 'react-dom'
|
|
354
|
+
import { compiler, html, parser } from 'markdown-to-jsx/html'
|
|
417
355
|
|
|
418
|
-
|
|
419
|
-
<Markdown options={{ overrides: { iframe: () => null } }}>
|
|
420
|
-
<iframe src="https://potentially-malicious-web-page.com/"></iframe>
|
|
421
|
-
</Markdown>,
|
|
422
|
-
document.body
|
|
423
|
-
)
|
|
356
|
+
const htmlString = compiler('# Hello world')
|
|
424
357
|
|
|
425
|
-
|
|
358
|
+
/** Or use parser + html */
|
|
359
|
+
const ast = parser('# Hello world')
|
|
360
|
+
const htmlString2 = html(ast)
|
|
426
361
|
```
|
|
427
362
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
#### options.overrides - Override Any HTML Tag's Representation
|
|
431
|
-
|
|
432
|
-
Pass 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.
|
|
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
|
-
)
|
|
363
|
+
#### Markdown
|
|
459
364
|
|
|
460
|
-
|
|
461
|
-
renders:
|
|
365
|
+
For markdown-to-markdown compilation (normalization and formatting), import from the `/markdown` entry point:
|
|
462
366
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
</div>
|
|
466
|
-
*/
|
|
467
|
-
```
|
|
367
|
+
```typescript
|
|
368
|
+
import { compiler, astToMarkdown, parser } from 'markdown-to-jsx/markdown'
|
|
468
369
|
|
|
469
|
-
|
|
370
|
+
const normalizedMarkdown = compiler('# Hello world\n\nExtra spaces!')
|
|
470
371
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
h1: MyParagraph,
|
|
475
|
-
},
|
|
476
|
-
}
|
|
372
|
+
/** Or work with AST */
|
|
373
|
+
const ast = parser('# Hello world')
|
|
374
|
+
const normalizedMarkdown2 = astToMarkdown(ast)
|
|
477
375
|
```
|
|
478
376
|
|
|
479
|
-
|
|
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
|
|
377
|
+
### Library Options
|
|
497
378
|
|
|
498
|
-
|
|
379
|
+
#### All Options
|
|
380
|
+
|
|
381
|
+
| Option | Type | Default | Description |
|
|
382
|
+
| ----------------------- | ----------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
383
|
+
| `createElement` | `function` | - | Custom createElement behavior (React/React Native/SolidJS/Vue only). See [createElement](#optionscreateelement) for details. |
|
|
384
|
+
| `disableAutoLink` | `boolean` | `false` | Disable automatic conversion of bare URLs to anchor tags. |
|
|
385
|
+
| `disableParsingRawHTML` | `boolean` | `false` | Disable parsing of raw HTML into JSX. |
|
|
386
|
+
| `enforceAtxHeadings` | `boolean` | `false` | Require space between `#` and header text (GFM spec compliance). |
|
|
387
|
+
| `forceBlock` | `boolean` | `false` | Force all content to be treated as block-level. |
|
|
388
|
+
| `forceInline` | `boolean` | `false` | Force all content to be treated as inline. |
|
|
389
|
+
| `forceWrapper` | `boolean` | `false` | Force wrapper even with single child (React/React Native/Vue only). See [forceWrapper](#optionsforcewrapper) for details. |
|
|
390
|
+
| `overrides` | `object` | - | Override HTML tag rendering. See [overrides](#optionsoverrides) for details. |
|
|
391
|
+
| `preserveFrontmatter` | `boolean` | `false` | Include frontmatter in rendered output (as `<pre>` for HTML/JSX, included in markdown). Behavior varies by compiler type. |
|
|
392
|
+
| `renderRule` | `function` | - | Custom rendering for AST rules. See [renderRule](#optionsrenderrule) for details. |
|
|
393
|
+
| `sanitizer` | `function` | built-in | Custom URL sanitizer function. See [sanitizer](#optionssanitizer) for details. |
|
|
394
|
+
| `slugify` | `function` | built-in | Custom slug generation for heading IDs. See [slugify](#optionsslugify) for details. |
|
|
395
|
+
| `tagfilter` | `boolean` | `true` | Escape dangerous HTML tags (`script`, `iframe`, `style`, etc.) to prevent XSS. |
|
|
396
|
+
| `wrapper` | `string \| component \| null` | `'div'` | Wrapper element for multiple children (React/React Native/Vue only). See [wrapper](#optionswrapper) for details. |
|
|
397
|
+
| `wrapperProps` | `object` | - | Props for wrapper element (React/React Native/Vue only). See [wrapperProps](#optionswrapperprops) for details. |
|
|
398
|
+
|
|
399
|
+
#### options.createElement
|
|
499
400
|
|
|
500
|
-
|
|
401
|
+
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
402
|
|
|
502
|
-
```
|
|
403
|
+
```javascript
|
|
503
404
|
import Markdown from 'markdown-to-jsx'
|
|
504
405
|
import React from 'react'
|
|
505
406
|
import { render } from 'react-dom'
|
|
506
407
|
|
|
507
|
-
import DatePicker from './date-picker'
|
|
508
|
-
|
|
509
408
|
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" />
|
|
409
|
+
# Hello world
|
|
516
410
|
`
|
|
517
411
|
|
|
518
412
|
render(
|
|
519
413
|
<Markdown
|
|
520
414
|
children={md}
|
|
521
415
|
options={{
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
416
|
+
createElement(type, props, children) {
|
|
417
|
+
return (
|
|
418
|
+
<div className="parent">
|
|
419
|
+
{React.createElement(type, props, children)}
|
|
420
|
+
</div>
|
|
421
|
+
)
|
|
526
422
|
},
|
|
527
423
|
}}
|
|
528
424
|
/>,
|
|
@@ -530,129 +426,61 @@ render(
|
|
|
530
426
|
)
|
|
531
427
|
```
|
|
532
428
|
|
|
533
|
-
|
|
429
|
+
#### options.forceWrapper
|
|
534
430
|
|
|
535
|
-
|
|
431
|
+
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
432
|
|
|
537
433
|
```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.
|
|
434
|
+
// Using `forceWrapper` with a single, inline child…
|
|
435
|
+
<Markdown options={{ wrapper: 'aside', forceWrapper: true }}>
|
|
436
|
+
Mumble, mumble…
|
|
437
|
+
</Markdown>
|
|
549
438
|
|
|
550
|
-
|
|
551
|
-
biasTowardDateTime="2017-12-05T07:39:36.091Z"
|
|
552
|
-
timezone="UTC+5"
|
|
553
|
-
startTime={1514579720511}
|
|
554
|
-
/>
|
|
555
|
-
`
|
|
439
|
+
// renders
|
|
556
440
|
|
|
557
|
-
|
|
558
|
-
<Markdown
|
|
559
|
-
children={md}
|
|
560
|
-
options={{
|
|
561
|
-
overrides: {
|
|
562
|
-
DatePicker: {
|
|
563
|
-
component: DatePicker,
|
|
564
|
-
},
|
|
565
|
-
},
|
|
566
|
-
}}
|
|
567
|
-
/>,
|
|
568
|
-
document.body
|
|
569
|
-
)
|
|
441
|
+
<aside>Mumble, mumble…</aside>
|
|
570
442
|
```
|
|
571
443
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
```tsx
|
|
575
|
-
import Markdown from 'markdown-to-jsx'
|
|
576
|
-
import React from 'react'
|
|
577
|
-
import { render } from 'react-dom'
|
|
578
|
-
import withProps from 'recompose/withProps'
|
|
579
|
-
|
|
580
|
-
import DatePicker from './date-picker'
|
|
444
|
+
#### options.overrides
|
|
581
445
|
|
|
582
|
-
|
|
583
|
-
range: {
|
|
584
|
-
start: new Date('2017-12-01'),
|
|
585
|
-
end: new Date('2017-12-31'),
|
|
586
|
-
},
|
|
587
|
-
timezone: 'UTC+5',
|
|
588
|
-
})(DatePicker)
|
|
446
|
+
Override HTML tag rendering or render custom React components. Three use cases:
|
|
589
447
|
|
|
590
|
-
|
|
591
|
-
# DatePicker
|
|
448
|
+
**1. Remove tags:** Return `null` to completely remove tags (beyond `tagfilter` escaping):
|
|
592
449
|
|
|
593
|
-
|
|
594
|
-
|
|
450
|
+
```tsx
|
|
451
|
+
<Markdown options={{ overrides: { iframe: () => null } }}>
|
|
452
|
+
<iframe src="..."></iframe>
|
|
453
|
+
</Markdown>
|
|
454
|
+
```
|
|
595
455
|
|
|
596
|
-
|
|
597
|
-
biasTowardDateTime="2017-12-05T07:39:36.091Z"
|
|
598
|
-
timezone="UTC+5"
|
|
599
|
-
startTime={1514579720511}
|
|
600
|
-
/>
|
|
456
|
+
**2. Override HTML tags:** Change component, props, or both:
|
|
601
457
|
|
|
602
|
-
|
|
458
|
+
```tsx
|
|
459
|
+
const MyParagraph = ({ children, ...props }) => <div {...props}>{children}</div>
|
|
603
460
|
|
|
604
|
-
<
|
|
605
|
-
|
|
461
|
+
<Markdown options={{ overrides: { h1: { component: MyParagraph, props: { className: 'foo' } } } }}>
|
|
462
|
+
# Hello
|
|
463
|
+
</Markdown>
|
|
606
464
|
|
|
607
|
-
|
|
608
|
-
<Markdown
|
|
609
|
-
children={md}
|
|
610
|
-
options={{
|
|
611
|
-
overrides: {
|
|
612
|
-
DatePicker,
|
|
613
|
-
DecemberDatePicker,
|
|
614
|
-
},
|
|
615
|
-
}}
|
|
616
|
-
/>,
|
|
617
|
-
document.body
|
|
618
|
-
)
|
|
465
|
+
/** Simplified */ { overrides: { h1: MyParagraph } }
|
|
619
466
|
```
|
|
620
467
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
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):
|
|
468
|
+
**3. Render React components:** Use custom components in markdown:
|
|
624
469
|
|
|
625
|
-
```
|
|
626
|
-
import
|
|
627
|
-
import React from 'react'
|
|
628
|
-
import { render } from 'react-dom'
|
|
470
|
+
```tsx
|
|
471
|
+
import DatePicker from './date-picker'
|
|
629
472
|
|
|
630
|
-
const md =
|
|
631
|
-
# Hello world
|
|
632
|
-
`
|
|
473
|
+
const md = `<DatePicker timezone="UTC+5" startTime={1514579720511} />`
|
|
633
474
|
|
|
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
|
-
)
|
|
475
|
+
<Markdown options={{ overrides: { DatePicker } }}>{md}</Markdown>
|
|
649
476
|
```
|
|
650
477
|
|
|
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).
|
|
478
|
+
**Important notes:**
|
|
654
479
|
|
|
655
|
-
|
|
480
|
+
- Props are passed as strings; parse them in your component (e.g., `JSON.parse(columns)`)
|
|
481
|
+
- JSX interpolations (`{value}`) are passed as raw strings
|
|
482
|
+
- Some props are preserved: `a` (`href`, `title`), `img` (`src`, `alt`, `title`), `input[type="checkbox"]` (`checked`, `readonly`), `ol` (`start`), `td`/`th` (`style`)
|
|
483
|
+
- Element mappings: `span` for inline text, `code` for inline code, `pre > code` for code blocks
|
|
656
484
|
|
|
657
485
|
#### options.renderRule
|
|
658
486
|
|
|
@@ -702,7 +530,7 @@ This can be overridden and replaced with a custom sanitizer if desired via `opti
|
|
|
702
530
|
// or
|
|
703
531
|
|
|
704
532
|
compiler('[foo](javascript:alert("foo"))', {
|
|
705
|
-
sanitizer:
|
|
533
|
+
sanitizer: value => value,
|
|
706
534
|
})
|
|
707
535
|
```
|
|
708
536
|
|
|
@@ -711,135 +539,51 @@ compiler('[foo](javascript:alert("foo"))', {
|
|
|
711
539
|
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
540
|
|
|
713
541
|
```tsx
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
// or
|
|
717
|
-
|
|
542
|
+
;<Markdown options={{ slugify: str => str }}># 中文</Markdown>
|
|
718
543
|
compiler('# 中文', { slugify: str => str })
|
|
719
|
-
|
|
720
|
-
// renders:
|
|
721
|
-
<h1 id="中文">中文</h1>
|
|
722
544
|
```
|
|
723
545
|
|
|
724
546
|
The original function is available as a library export called `slugify`.
|
|
725
547
|
|
|
726
|
-
#### options.
|
|
548
|
+
#### options.wrapper
|
|
727
549
|
|
|
728
|
-
|
|
550
|
+
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
551
|
|
|
730
552
|
```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
|
-
)
|
|
553
|
+
const str = '# Heck Yes\n\nThis is great!'
|
|
741
554
|
|
|
742
|
-
|
|
555
|
+
<Markdown options={{ wrapper: 'article' }}>{str}</Markdown>
|
|
743
556
|
|
|
744
|
-
|
|
745
|
-
The URL https://quantizor.dev will not be rendered as an anchor tag.
|
|
746
|
-
</span>
|
|
557
|
+
compiler(str, { wrapper: 'article' })
|
|
747
558
|
```
|
|
748
559
|
|
|
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
|
-
```
|
|
560
|
+
##### Other useful recipes
|
|
782
561
|
|
|
783
|
-
|
|
562
|
+
To get an array of children back without a wrapper, set `wrapper` to `null`. This is particularly useful when using `compiler(…)` directly.
|
|
784
563
|
|
|
785
564
|
```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"
|
|
565
|
+
compiler('One\n\nTwo\n\nThree', { wrapper: null })[
|
|
566
|
+
/** Returns */ ((<p>One</p>), (<p>Two</p>), (<p>Three</p>))
|
|
567
|
+
]
|
|
802
568
|
```
|
|
803
569
|
|
|
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
|
-
```
|
|
570
|
+
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
571
|
|
|
822
|
-
#### options.
|
|
572
|
+
#### options.wrapperProps
|
|
823
573
|
|
|
824
|
-
|
|
574
|
+
Props to apply to the wrapper element when `wrapper` is used.
|
|
825
575
|
|
|
826
576
|
```tsx
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
// React output: <script></script>
|
|
577
|
+
<Markdown
|
|
578
|
+
options={{
|
|
579
|
+
wrapper: 'article',
|
|
580
|
+
wrapperProps: { className: 'post', 'data-testid': 'markdown-content' },
|
|
581
|
+
}}
|
|
582
|
+
>
|
|
583
|
+
# Hello World
|
|
584
|
+
</Markdown>
|
|
836
585
|
```
|
|
837
586
|
|
|
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
587
|
### Syntax highlighting
|
|
844
588
|
|
|
845
589
|
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 +620,7 @@ function App() {
|
|
|
876
620
|
}
|
|
877
621
|
|
|
878
622
|
function SyntaxHighlightedCode(props) {
|
|
879
|
-
const ref =
|
|
623
|
+
const ref = React.useRef<HTMLElement | null>(null)
|
|
880
624
|
|
|
881
625
|
React.useEffect(() => {
|
|
882
626
|
if (ref.current && props.className?.includes('lang-') && window.hljs) {
|
|
@@ -933,24 +677,10 @@ function Example() {
|
|
|
933
677
|
</Markdown>
|
|
934
678
|
)
|
|
935
679
|
}
|
|
936
|
-
|
|
937
|
-
// renders
|
|
938
|
-
// <span>On a beautiful summer day, all I want to do is <span>🙂</span>.</span>
|
|
939
680
|
```
|
|
940
681
|
|
|
941
682
|
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
683
|
|
|
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
684
|
### Usage with Preact
|
|
955
685
|
|
|
956
686
|
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,88 +852,24 @@ if (node.type === RuleType.heading) {
|
|
|
1122
852
|
|
|
1123
853
|
### Gotchas
|
|
1124
854
|
|
|
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.
|
|
855
|
+
**Props are stringified:** When using `overrides` with React components, props are passed as strings. Parse them in your component:
|
|
1128
856
|
|
|
1129
857
|
```tsx
|
|
1130
|
-
const Table
|
|
1131
|
-
JSX.IntrinsicElements['table'] & {
|
|
1132
|
-
columns: string
|
|
1133
|
-
dataSource: string
|
|
1134
|
-
}
|
|
1135
|
-
> = ({ columns, dataSource, ...props }) => {
|
|
858
|
+
const Table = ({ columns, dataSource, ...props }) => {
|
|
1136
859
|
const parsedColumns = JSON.parse(columns)
|
|
1137
860
|
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
|
-
)
|
|
861
|
+
// ... use parsed data
|
|
1152
862
|
}
|
|
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
|
-
```
|
|
1170
|
-
|
|
1171
|
-
#### Significant indentation inside arbitrary HTML
|
|
1172
|
-
|
|
1173
|
-
People usually write HTML like this:
|
|
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
863
|
```
|
|
1186
864
|
|
|
1187
|
-
|
|
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
|
-
```
|
|
865
|
+
**HTML indentation:** Leading whitespace in HTML blocks is auto-trimmed based on the first line's indentation to avoid markdown syntax conflicts.
|
|
1200
866
|
|
|
1201
|
-
|
|
867
|
+
**Code in HTML:** Don't put code directly in HTML divs. Use fenced code blocks instead:
|
|
1202
868
|
|
|
1203
869
|
````md
|
|
1204
870
|
<div>
|
|
1205
871
|
```js
|
|
1206
|
-
var
|
|
872
|
+
var code = here();
|
|
1207
873
|
```
|
|
1208
874
|
</div>
|
|
1209
875
|
````
|