Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 93 additions & 11 deletions docs/library/typography/markdown.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,19 @@ rx.markdown("Pythagorean theorem: $a^2 + b^2 = c^2$.")

You can render code blocks with syntax highlighting using the \`\`\`\{language} syntax:

```python demo
````python demo
rx.markdown(
r"""
\```python
```python
import reflex as rx
from .pages import index

app = rx.App()
app.add_page(index)
\```
```
"""
)
```
````

## Tables

Expand All @@ -64,6 +64,86 @@ rx.markdown(
)
```

## Plugins

Plugins can be used to extend the functionality of the markdown renderer.

By default Reflex uses the following plugins:
- `remark-gfm` for Github Flavored Markdown support (`use_gfm`).
- `remark-math` and `rehype-katex` for math equation support (`use_math`, `use_katex`).
- `rehype-unwrap-images` to remove paragraph tags around images (`use_unwrap_images`).
- `rehype-raw` to render raw HTML in markdown (`use_raw`). NOTE: in a future release this will be disabled by default for security reasons.

These default plugins can be disabled by passing `use_[plugin_name]=False` to the `rx.markdown` component. For example, to disable raw HTML rendering, use `rx.markdown(..., use_raw=False)`.

## Arbitrary Plugins

You can also add arbitrary remark or rehype plugins using the `remark_plugins`
and `rehype_plugins` props in conjunction with the `rx.markdown.plugin` helper.

`rx.markdown.plugin` takes two arguments:

1. The npm package name and version of the plugin (e.g. `[email protected]`).
2. The named export to use from the plugin (e.g. `remarkEmoji`).

### Remark Plugin Example

For example, to add support for emojis using the `remark-emoji` plugin:

```python demo
rx.markdown(
"Hello :smile:! :rocket: :tada:",
remark_plugins=[
rx.markdown.plugin("[email protected]", "remarkEmoji"),
],
)
```

### Rehype Plugin Example

To make `rehype-raw` safer for untrusted HTML input we can use `rehype-sanitize`, which defaults to a safe schema similar to that used by Github.

```python demo
rx.markdown(
"""Here is some **bold** text and a <script>alert("XSS Attack!")</script>.""",
use_raw=True,
rehype_plugins=[
rx.markdown.plugin("[email protected]", "rehypeSanitize"),
],
)
```

### Plugin Options

Both `remark_plugins` and `rehype_plugins` accept a heterogeneous list of `plugin`
or tuple of `(plugin, options)` in case the plugin requires some kind of special
configuration.

For example, `rehype-slug` is a simple plugin that adds ID attributes to the
headings, but the `rehype-autolink-headings` plugin accepts options to specify
how to render the links to those anchors.

```python demo
rx.markdown(
"""
# Heading 1
## Heading 2
""",
rehype_plugins=[
rx.markdown.plugin("[email protected]", "rehypeSlug"),
(
rx.markdown.plugin("[email protected]", "rehypeAutolinkHeadings"),
{
"behavior": "wrap",
"properties": {
"className": ["heading-link"],
},
},
),
],
)
```

## Component Map

You can specify which components to use for rendering markdown elements using the
Expand All @@ -73,17 +153,17 @@ Each key in the `component_map` prop is a markdown element, and the value is
a function that takes the text of the element as input and returns a Reflex component.

```md alert
The `codeblock` and `a` tags are special cases. In addition to the `text`, they also receive a `props` argument containing additional props for the component.
The `pre` and `a` tags are special cases. In addition to the `text`, they also receive a `props` argument containing additional props for the component.
```

```python demo exec
````python demo exec
component_map = {
"h1": lambda text: rx.heading(text, size="5", margin_y="1em"),
"h2": lambda text: rx.heading(text, size="3", margin_y="1em"),
"h3": lambda text: rx.heading(text, size="1", margin_y="1em"),
"p": lambda text: rx.text(text, color="green", margin_y="1em"),
"code": lambda text: rx.code(text, color="purple"),
"codeblock": lambda text, **props: rx.code_block(text, **props, theme=rx.code_block.themes.dark, margin_y="1em"),
"pre": lambda text, **props: rx.code_block(text, **props, theme=rx.code_block.themes.dark, margin_y="1em"),
"a": lambda text, **props: rx.link(text, **props, color="blue", _hover={"color": "red"}),
}

Expand All @@ -99,15 +179,17 @@ r"""

Here is some `code`:

\```python
```python
import reflex as rx

component = rx.text("Hello World!")
\```
```

And then some more text here, followed by a link to [Reflex](https://reflex.dev/).
And then some more text here,
followed by a link to
[Reflex](https://reflex.dev/).
""",
component_map=component_map,
)
)
```
````
19 changes: 16 additions & 3 deletions pcweb/flexdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,17 @@ class DemoBlockDark(DemoBlock):
theme = "dark"


class DemoBlockNestedMarkdown(DemoBlock):
"""Used when the block contains literal markdown with triple backticks."""

starting_indicator = "````python demo"
ending_indicator = "````"


class DemoBlockNestedMarkdownDark(DemoBlockNestedMarkdown):
theme = "dark"


class VideoBlock(flexdown.blocks.MarkdownBlock):
"""A block that displays a video."""

Expand Down Expand Up @@ -602,11 +613,11 @@ def render(self, env) -> rx.Component:
"li": lambda text: list_comp(text=text),
"a": doclink2,
"code": lambda text: code_comp(text=text),
"codeblock": code_block_markdown,
"pre": code_block_markdown,
"img": lambda src: img_comp_xd(src=src),
}
comp2 = component_map.copy()
comp2["codeblock"] = code_block_markdown_dark
comp2["pre"] = code_block_markdown_dark
comp2["ul"] = lambda items: unordered_list_comp(items=items)
comp2["ol"] = lambda items: ordered_list_comp(items=items)

Expand All @@ -615,6 +626,7 @@ def render(self, env) -> rx.Component:
block_types=[
DemoOnly,
DemoBlock,
DemoBlockNestedMarkdown,
AlertBlock,
DefinitionBlock,
SectionBlock,
Expand All @@ -628,6 +640,7 @@ def render(self, env) -> rx.Component:
xd2 = flexdown.Flexdown(
block_types=[
DemoBlockDark,
DemoBlockNestedMarkdownDark,
AlertBlock,
DefinitionBlock,
SectionBlock,
Expand Down Expand Up @@ -659,6 +672,6 @@ def markdown_with_shiki(*args, **kwargs):
"""
return rx.markdown(
*args,
component_map={"codeblock": markdown_codeblock},
component_map={"pre": markdown_codeblock},
**kwargs,
)
Loading