markdown-to-jsx 9.8.1 → 9.9.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 +89 -6
- package/dist/html.cjs +47 -41
- package/dist/html.d.cts +36 -4
- package/dist/html.d.ts +36 -4
- package/dist/html.js +47 -41
- package/dist/html.js.map +7 -7
- package/dist/index.cjs +45 -42
- package/dist/index.d.cts +36 -4
- package/dist/index.d.ts +36 -4
- package/dist/index.js +45 -42
- package/dist/index.js.map +7 -7
- package/dist/markdown.cjs +72 -59
- package/dist/markdown.d.cts +36 -4
- package/dist/markdown.d.ts +36 -4
- package/dist/markdown.js +72 -59
- package/dist/markdown.js.map +7 -7
- package/dist/native.cjs +44 -41
- package/dist/native.d.cts +48 -4
- package/dist/native.d.ts +48 -4
- package/dist/native.js +44 -41
- package/dist/native.js.map +7 -7
- package/dist/react.cjs +45 -42
- package/dist/react.d.cts +36 -4
- package/dist/react.d.ts +36 -4
- package/dist/react.js +45 -42
- package/dist/react.js.map +7 -7
- package/dist/solid.cjs +44 -41
- package/dist/solid.d.cts +36 -4
- package/dist/solid.d.ts +36 -4
- package/dist/solid.js +44 -41
- package/dist/solid.js.map +7 -7
- package/dist/vue.cjs +44 -41
- package/dist/vue.d.cts +37 -5
- package/dist/vue.d.ts +37 -5
- package/dist/vue.js +44 -41
- package/dist/vue.js.map +7 -7
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -40,6 +40,7 @@ Some special features of the library:
|
|
|
40
40
|
- [options.ignoreHTMLBlocks](#optionsignorehtmlblocks)
|
|
41
41
|
- [options.renderRule](#optionsrenderrule)
|
|
42
42
|
- [options.sanitizer](#optionssanitizer)
|
|
43
|
+
- [Raw HTML sanitization](#raw-html-sanitization)
|
|
43
44
|
- [options.slugify](#optionsslugify)
|
|
44
45
|
- [options.wrapper](#optionswrapper)
|
|
45
46
|
- [Other useful recipes](#other-useful-recipes)
|
|
@@ -303,17 +304,81 @@ function App() {
|
|
|
303
304
|
- `styles?: NativeStyles` - Per-key style overrides keyed by element type. Each key is narrowed to the style accepted by its target component (`TextStyle` for inline content and headings, `ViewStyle` for containers, `ImageStyle` for images).
|
|
304
305
|
- `wrapperProps?: ViewProps | TextProps` - Props for the wrapper component (defaults to `View` for block, `Text` for inline)
|
|
305
306
|
|
|
307
|
+
**Default styles:**
|
|
308
|
+
|
|
309
|
+
React Native output ships with a clean, minimal base stylesheet so markdown renders with a readable hierarchy out of the box: a heading size cascade, monospace code, spacing between blocks, a blockquote rule, and a table with a header row and aligned columns. Everything stays customizable. Each key in `styles` merges over the default for that element, so setting one property (say `heading1.color`) keeps the rest of the default. `styles.text` is a base applied under all rendered text, so you can set the font, color, and size for the whole document at once:
|
|
310
|
+
|
|
311
|
+
```tsx
|
|
312
|
+
<Markdown options={{ styles: { text: { color: '#333', fontSize: 17 } } }}>{content}</Markdown>
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Override any element by its key. Every key is optional and merges over the default, so you change only the properties you name:
|
|
316
|
+
|
|
317
|
+
```tsx
|
|
318
|
+
<Markdown
|
|
319
|
+
options={{
|
|
320
|
+
styles: {
|
|
321
|
+
heading1: { color: '#b91c1c' }, // change one property, keep the rest of the default
|
|
322
|
+
blockquote: { borderLeftColor: '#b91c1c' },
|
|
323
|
+
codeInline: { backgroundColor: '#f4f4f5' },
|
|
324
|
+
},
|
|
325
|
+
}}
|
|
326
|
+
>
|
|
327
|
+
{content}
|
|
328
|
+
</Markdown>
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
The available keys, each typed to the style its target component accepts (`TextStyle`, `ViewStyle`, or `ImageStyle`):
|
|
332
|
+
|
|
333
|
+
- Text: `text` (the base under all rendered text), `paragraph`, `heading1` through `heading6`, `link`, `footnote`, `codeInline`, `strong`, `em`, `del`, `mark`, `listItemBullet`, `listItemNumber`
|
|
334
|
+
- Blocks: `blockquote`, `codeBlock`, `thematicBreak`, `image`
|
|
335
|
+
- Lists: `listOrdered`, `listUnordered`, `listItem`
|
|
336
|
+
- Tables: `table`, `tableHeader`, `tableHeaderCell`, `tableHeaderText` (the bold header run), `tableRow`, `tableCell`, `tableCellDivider` and `tableRowDivider` (the grid lines)
|
|
337
|
+
- GFM task: `gfmTask` (the drawn checkbox), `gfmTaskChecked` (the checked-state accent fill), `checkmark` (the checkmark glyph)
|
|
338
|
+
|
|
339
|
+
Raw HTML container tags (`div`, `section`, `article`, `ul`, `ol`, `li`, `th`, `td`, and similar) also take a style under their own tag name.
|
|
340
|
+
|
|
341
|
+
To replace an element's rendering entirely rather than restyle it, use `overrides`. Here is a fully themed configuration combining styles and overrides, plus a `renderRule` that swaps fenced code for a syntax highlighter:
|
|
342
|
+
|
|
343
|
+
```tsx
|
|
344
|
+
const Callout = ({ children }) => (
|
|
345
|
+
<View style={{ borderLeftColor: '#3fb950', borderLeftWidth: 4, paddingLeft: 12 }}>{children}</View>
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
<Markdown
|
|
349
|
+
options={{
|
|
350
|
+
styles: {
|
|
351
|
+
text: { color: '#c9d1d9' }, // base color for all text
|
|
352
|
+
heading1: { color: '#f0f6fc', fontSize: 26 },
|
|
353
|
+
link: { color: '#58a6ff', textDecorationLine: 'none' },
|
|
354
|
+
},
|
|
355
|
+
overrides: { blockquote: { component: Callout } },
|
|
356
|
+
wrapperProps: { style: { backgroundColor: '#0d1117', padding: 12 } },
|
|
357
|
+
renderRule(next, node, _renderChildren, state) {
|
|
358
|
+
if (node.type === RuleType.codeBlock) {
|
|
359
|
+
return <MySyntaxHighlighter key={state.key} code={node.text} lang={node.lang} />
|
|
360
|
+
}
|
|
361
|
+
return next()
|
|
362
|
+
},
|
|
363
|
+
}}
|
|
364
|
+
>
|
|
365
|
+
{content}
|
|
366
|
+
</Markdown>
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
A fenced code block renders as `<pre><code>`, and neither element receives the fence's language, so language-aware code rendering (syntax highlighting, a KaTeX block for ```` ```latex ````) belongs in `renderRule`, where `node.lang` and `node.text` are available.
|
|
370
|
+
|
|
306
371
|
**Overrides:**
|
|
307
372
|
|
|
308
|
-
Overrides on native work the same as on web
|
|
373
|
+
Overrides on native work the same as on web: `overrides` keys correspond to HTML tag names and fire for parsed markdown as well as raw HTML. For example, override `code` to swap inline backticks and the inner element of fenced code blocks, override `pre` to wrap fenced code, override `input` to render real checkbox visuals for GFM tasks, and override `ul`/`ol`/`li` to swap list containers and rows. Bullets and numbers remain library-controlled inside `li`.
|
|
309
374
|
|
|
310
|
-
When both a renderer-supplied style (`styles.codeInline`, `styles.gfmTask`, etc.) and `overrides[tag].props.style` are set, they merge as a React Native style array
|
|
375
|
+
When both a renderer-supplied style (`styles.codeInline`, `styles.gfmTask`, etc.) and `overrides[tag].props.style` are set, they merge as a React Native style array, and override-level styling wins on conflict.
|
|
311
376
|
|
|
312
377
|
**GFM task checkboxes:**
|
|
313
378
|
|
|
314
|
-
Task checkboxes (`- [x]`, `- [ ]`) route through an `<input type="checkbox">` tag that maps to `View` by default
|
|
379
|
+
Task checkboxes (`- [x]`, `- [ ]`) route through an `<input type="checkbox">` tag that maps to `View` by default and renders a drawn checkbox: an outlined box that fills with an accent color and a checkmark when the task is done. The checkbox stands in for the list bullet, which is suppressed for task items. Restyle the box with `styles.gfmTask`, or override `input` to replace the visual entirely (for a real `<Image>` checkbox, animated state, etc.). Your override receives `checked`, `type: 'checkbox'`, `readOnly`, the merged `style`, and a `<Text>` child rendering `[x]` or `[ ]` as a fallback marker; consumers that fully customize the visual should ignore the child and render their own indicator from `props.checked`.
|
|
315
380
|
|
|
316
|
-
The list item wrapper around a task gets `flexDirection: 'row'` and `alignItems: '
|
|
381
|
+
The list item wrapper around a task gets `flexDirection: 'row'` and `alignItems: 'flex-start'` applied by default, so the checkbox lines up with the first line of the label; the checkbox's own top margin then centers it against that line, which keeps it correctly placed even when the label wraps to multiple lines. Override these by passing your own `styles.listItem`: `mergeStyle` keeps the row defaults underneath, so any property you set wins on collision (e.g. supply `alignItems: 'center'` to vertically center the checkbox against the whole label instead).
|
|
317
382
|
|
|
318
383
|
**HTML Tag Mapping:**
|
|
319
384
|
HTML tags are automatically mapped to React Native components:
|
|
@@ -323,8 +388,12 @@ HTML tags are automatically mapped to React Native components:
|
|
|
323
388
|
- Inline elements (`<span>`, `<strong>`, `<em>`, `<a>`, headings, `<code>`, etc.) → `Text` component
|
|
324
389
|
- Type 1 blocks (`<pre>`, `<script>`, `<style>`, `<textarea>`) → `View` component
|
|
325
390
|
|
|
391
|
+
**Mixing text and blocks:** React Native cannot nest an image or view inside text, so an inline container (a paragraph, heading, emphasis, or link) that holds an image or block-level content renders as a `View` instead of a `Text`. Its text is grouped into a `Text` so it still flows on one line, the image renders as its own element, and an image inside a link stays tappable through a `Pressable`. Text-only content is unaffected and renders as before.
|
|
392
|
+
|
|
326
393
|
**Note:** Links are underlined by default for better accessibility and discoverability. You can override this via the `styles.link` option.
|
|
327
394
|
|
|
395
|
+
**Note:** Footnote reference markers (`[^1]`) render as a superscript, matching the `<sup>` the web renderers emit. React Native cannot raise or shrink inline text through styles (it ignores `verticalAlign` and transforms on inline text), so numeric markers use Unicode superscript glyphs (`¹²³`), which the font draws raised and sized relative to the surrounding text on their own. Non-numeric identifiers (`[^note]`) render as plain text. Style the marker via the `styles.footnote` option.
|
|
396
|
+
|
|
328
397
|
#### SolidJS
|
|
329
398
|
|
|
330
399
|
For SolidJS usage, import from the `/solid` entry point:
|
|
@@ -708,6 +777,20 @@ compiler('[foo](javascript:alert("foo"))', {
|
|
|
708
777
|
})
|
|
709
778
|
```
|
|
710
779
|
|
|
780
|
+
#### Raw HTML sanitization
|
|
781
|
+
|
|
782
|
+
Raw HTML in your markdown is sanitized automatically in every renderer (React, React Native, HTML, Markdown, Solid, and Vue), so untrusted input cannot smuggle a script into your output. This is always on and independent of `options.sanitizer`, which governs URL schemes alone.
|
|
783
|
+
|
|
784
|
+
Removed before rendering:
|
|
785
|
+
|
|
786
|
+
- Inline event handlers: `onclick`, `onerror`, `onload`, and any other `on*` attribute.
|
|
787
|
+
- URL attributes carrying a `javascript:`, `vbscript:`, or non-image `data:` scheme, in `href`, `src`, `action`, `formaction`, `poster`, `cite`, `background`, `data`, `longdesc`, and `xlink:href`. Schemes hidden behind HTML entities such as `java	script:` or a semicolon-less `javascript:` are decoded and caught too.
|
|
788
|
+
- The `iframe` `srcdoc` attribute.
|
|
789
|
+
|
|
790
|
+
Kept: safe attributes with their original formatting, `data:image` URLs, and event handlers you pass as expressions to your own components (`<MyButton onClick={fn} />`) along with bare boolean props on them (`<MyButton onClick />`). One caveat: `data:image/svg+xml` is allowed but can execute script when opened as a top-level navigation, so treat SVG data URLs from untrusted sources with care.
|
|
791
|
+
|
|
792
|
+
Dangerous tag names (`script`, `iframe`, `style`, and similar) are escaped separately by the [`tagfilter`](#all-options) option, which is on by default.
|
|
793
|
+
|
|
711
794
|
#### options.slugify
|
|
712
795
|
|
|
713
796
|
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:
|
|
@@ -860,7 +943,7 @@ When you use `options.renderRule`, any React-renderable JSX may be returned incl
|
|
|
860
943
|
|
|
861
944
|
When rendering markdown content that arrives incrementally (e.g., from an AI/LLM API, WebSocket, or Server-Sent Events), you may notice raw markdown syntax briefly appearing before it renders properly. This happens because incomplete syntax like `**bold text` or `<CustomComponent>partial content` gets rendered as text before the closing delimiter arrives.
|
|
862
945
|
|
|
863
|
-
The `optimizeForStreaming` option solves this by detecting incomplete markdown structures and returning `null`
|
|
946
|
+
The `optimizeForStreaming` option solves this by detecting incomplete markdown structures and holding them back until the content is complete (returning `null` on React and React Native, an empty string on HTML). It works across every renderer:
|
|
864
947
|
|
|
865
948
|
```tsx
|
|
866
949
|
import Markdown from 'markdown-to-jsx/react'
|
|
@@ -899,7 +982,7 @@ function ChatMessage({ stream }) {
|
|
|
899
982
|
- Unclosed bold/italic (`**text` or `*text` without closing)
|
|
900
983
|
- Unclosed strikethrough (`~~text` without closing `~~`)
|
|
901
984
|
- Unclosed links (`[text](url` without closing `)`)
|
|
902
|
-
- Incomplete tables (header
|
|
985
|
+
- Incomplete tables (a header or divider row before the first data row); once the table renders, it stays put as further rows stream in, so it never flashes raw pipes or flickers between rows
|
|
903
986
|
|
|
904
987
|
**What renders normally (content visible as it streams):**
|
|
905
988
|
|