| ... | @@ -25,16 +25,25 @@ to Streamdown: | ... | @@ -25,16 +25,25 @@ to Streamdown: |
| 25 | as adjacent content changes. This is done to preserve remounts for things like | 25 | as adjacent content changes. This is done to preserve remounts for things like |
| 26 | custom `<a>` tags or other components. (For example, if a custom `<a>` fetches | 26 | custom `<a>` tags or other components. (For example, if a custom `<a>` fetches |
| 27 | previewing data and provides a hover card, that card won't flicker). | 27 | previewing data and provides a hover card, that card won't flicker). |
| | 28 | - **Better Prediction**: While optimizing edge cases, the prediction system became |
| | 29 | better than the Streamdown one on accident. |
| 28 | - **Headless UI**: No built in styles or components, bring your own CSS to blend | 30 | - **Headless UI**: No built in styles or components, bring your own CSS to blend |
| 29 | your markdown with your existing theme. `@clo/react-markdown` simply takes in | 31 | your markdown with your existing theme. `@clo/react-markdown` simply takes in |
| 30 | your existing `unified` pipeline and works off of that. | 32 | your existing `unified` pipeline and works off of that. |
| 31 | - **Easy to Audit**: ~850 lines of TypeScript. Main dependency is [unified]. | 33 | - **Easy to Audit**: Only ~1000 lines of modern TypeScript. 4 total files. |
| 32 | | 34 | |
| 33 | [Streamdown]: https://streamdown.ai | 35 | [Streamdown]: https://streamdown.ai |
| 34 | [unified]: https://unifiedjs.com/ | 36 | [unified]: https://unifiedjs.com/ |
| 35 | | 37 | |
| 36 | ## Getting Started | 38 | ## Getting Started |
| 37 | | 39 | |
| | 40 | Install off of the [JSR package](https://jsr.io/clo/react-markdown): |
| | 41 | |
| | 42 | ```sh |
| | 43 | npx jsr add @clo/react-markdown |
| | 44 | pnpm add jsr:@clo/react-markdown |
| | 45 | ``` |
| | 46 | |
| 38 | ```tsx | 47 | ```tsx |
| 39 | import { Markdown } from "@clo/react-markdown"; | 48 | import { Markdown } from "@clo/react-markdown"; |
| 40 | import remarkGfm from "remark-gfm"; | 49 | import remarkGfm from "remark-gfm"; |
| ... | @@ -70,6 +79,15 @@ export function HelloWorld() { | ... | @@ -70,6 +79,15 @@ export function HelloWorld() { |
| 70 | } | 79 | } |
| 71 | ``` | 80 | ``` |
| 72 | | 81 | |
| | 82 | If you want to play with the example project, which offers a comparison to |
| | 83 | Streamdown and the non-memoized `react-markdown` package. |
| | 84 | |
| | 85 | ``` |
| | 86 | git clone https://git.paperclover.net/clo/react-markdown |
| | 87 | cd react-markdown |
| | 88 | pnpm demo # auto-install |
| | 89 | ``` |
| | 90 | |
| 73 | ## Architecture | 91 | ## Architecture |
| 74 | | 92 | |
| 75 | The `Markdown` component is built out of a memoizer which uses the following tricks to improve performance: | 93 | The `Markdown` component is built out of a memoizer which uses the following tricks to improve performance: |
| ... | @@ -89,7 +107,8 @@ The `Markdown` component is built out of a memoizer which uses the following tri | ... | @@ -89,7 +107,8 @@ The `Markdown` component is built out of a memoizer which uses the following tri |
| 89 | it means any places that might have used a reference link may now have to | 107 | it means any places that might have used a reference link may now have to |
| 90 | reflect it. | 108 | reflect it. |
| 91 | - After all that, a special AST -> React node transform is used that diffs the | 109 | - After all that, a special AST -> React node transform is used that diffs the |
| 92 | new ast with the last ast, reusing React nodes whenever possible. This is what | 110 | new ast with the last ast, reusing React nodes whenever possible. It supports |
| 93 | prevents most rerenders. | 111 | nested children as well as re-ordering top level blocks. This is what prevents |
| | 112 | most rerenders and is the "secret sauce". |
| 94 | | 113 | |
| 95 | All put together, basically nothing rerenders except what actually changed, and the document is beautiful. | 114 | All put together, basically nothing rerenders except what actually changed, and the document is beautiful. |