# Markdown Renderer

Source: https://developers.inkdrop.app/modules/markdown-renderer

Inkdrop's markdown rendering system is built on top of [remark](https://github.com/remarkjs/remark), a markdown processor powered by [plugins](https://github.com/remarkjs/remark/tree/master/doc/plugins.md) part of [the unified collective](https://github.com/unifiedjs/unified).
Available as `env.markdownRenderer` on the [Environment](https://developers.inkdrop.app/modules/environment) passed to `activate(env)`.

It is also exported from the `inkdrop` module:

```js
const { markdownRenderer } = require('inkdrop')
```

> **Note:**
>
> **Compatibility note**: Inkdrop (>=5.6.0) uses **unified\@10**, **remark\@14**, **remark-rehype\@10**.

---

## remarkPlugins

An array of custom [Remark plugins](https://github.com/remarkjs/remark#plugins).

**See also**: [math plugin](https://github.com/inkdropapp/inkdrop-math)

Here is a plugin that adds your custom Remark plugin:

```js
const { markdownRenderer } = require('inkdrop')

module.exports = {
  activate() {
    if (markdownRenderer) {
      markdownRenderer.remarkPlugins.push(YOUR_PLUGIN)
    }
  },
  deactivate() {
    if (markdownRenderer) {
      markdownRenderer.remarkPlugins = markdownRenderer.remarkPlugins.filter(
        plugin => YOUR_PLUGIN !== plugin
      )
    }
  }
}
```

---

## rehypePlugins

The Markdown notes are converted from Markdown to HTML.
The HTML data is then processed with [Rehype](https://github.com/rehypejs/rehype).
You can extend the process by adding your custom [Rehype plugins](https://github.com/rehypejs/rehype#plugins).

Here is a plugin that adds your custom Rehype plugin:

```js
const { markdownRenderer } = require('inkdrop')

module.exports = {
  activate() {
    if (markdownRenderer) {
      markdownRenderer.rehypePlugins.push(YOUR_PLUGIN)
    }
  },
  deactivate() {
    if (markdownRenderer) {
      markdownRenderer.rehypePlugins = markdownRenderer.remarkPlugins.filter(
        plugin => YOUR_PLUGIN !== plugin
      )
    }
  }
}
```

---

## remarkReactComponents

This property is an object that maps HTML element names to React components.
You can override default elements (such as `a`, `p`, etc.) by passing your custom components.

Check out [rehype-react](https://github.com/rehypejs/rehype-react#optionscomponents) for more details.

- Example plugin: [toc](https://github.com/inkdropapp/inkdrop-toc)

> **Warning:**
>
> Do not use `remarkReactComponents` to override the click events of `a` HTML elements.
> Instead, use the `events` property.

If you want to override the rendering of `a` HTML elements with a custom component, you can do it as shown:

```js
class Anchor extends React.Component {
  render() {
    // ...
  }
}
markdownRenderer.remarkReactComponents.a = Anchor
```

---

## remarkCodeComponents

Allows you to render code blocks with custom React components.
You can define a component for a specific language as demonstrated:

- Example plugin: [mermaid](https://github.com/inkdropapp/inkdrop-mermaid)

```js
const JSCodeBlock = props => {
  const { lang, title, lineAt } = props
  const code = props.children[0]
  return (
    <pre>
      {/* Custom JS block */}
    </pre>
  )
}

markdownRenderer.remarkCodeComponents['javascript'] = JSCodeBlock
```

---

## events

The `events` property allows you to listen to events emitted by the renderer.
An event handler can be an `async` function.
Here is a list of events that you can listen to:

- **a:click**

  Emitted when `a` link is clicked. Returns `true` if the event is handled.
  - **event** (React.MouseEvent\<HTMLAnchorElement, MouseEvent>)

    Mouse event
- **checkbox:change**

  Emitted when `checkbox` is clicked. Returns `true` if the event is handled.
  - **event** (React.ChangeEvent\<HTMLInputElement>)

    Change event
  - **detail** ({ checked: boolean numLine: number })

    `checked` indicates whether the checkbox is checked or not.
    `numLine` is the line number of the checkbox.

```js
useEffect(() => {
  const app = getApp()
  const disposables = [
    events.on('checkbox:change', (_e, { checked, numLine }) => {
      console.log(`checkbox ${checked ? 'checked' : 'unchecked'} at line ${numLine}`)
    }),
    events.on('a:click', e => {
      const target = e.target as HTMLAnchorElement
      const { href } = target

      console.log(`clicked link: ${href}`)
      // Should return true if the event is handled
      return true
    })
  ]
  return () => {
    disposables.forEach(d => d())
  }
}, [])
```
