natemoo-re / astro-remote

Render remote HTML or Markdown content in Astro with full control over the output
MIT License
181 stars 9 forks source link

Astro Remote

NPM Version

Render remote HTML or Markdown content in Astro with full control over the output.

Powered by ultrahtml and marked.

Install

npm install astro-remote
pnpm install astro-remote
yarn install astro-remote

Rendering Remote Content

The most basic function of astro-remote is to convert a string of HTML or Markdown to HTML. Use the Markup and Markdown components depending on your input.

---
import { Markup, Markdown } from 'astro-remote';
const { html, markdown } = await fetch('http://my-site.com/api/v1/post').then(res => res.json());
---

<Markup content={html} />
<Markdown content={markdown} />

Sanitization

By default, all HTML content will be sanitized with sensible defaults (script blocks are dropped). This can be controlled using the SanitizeOptions available in ultrahtml. Set to false to disable sanitization.

---
import { Markup } from 'astro-remote';
const content = await fetch('http://my-site.com/api/v1/post').then(res => res.text());
---

<!-- Disallow `head` and `style` attributes, and standard formatting from host website -->
<Markup 
    content={content} 
    sanitize={{ 
        dropElements: ["head","style"], 
        blockElements: ["html", "body", "div"],
    }} 
/>

Customization

Both Markup and Markdown allow full control over the rendering of output. The components option allows you to replace a standard HTML element with a custom component.

---
import { Markdown, Markup } from 'astro-remote';
import Title from '../components/Title.astro';
const content = await fetch('http://my-site.com/api/v1/post').then(res => res.text());
---

<!-- Render <h1> as custom <Title> component -->
<Markup content={content} components={{ h1: Title }} />
<Markdown content={content} components={{ h1: Title }} />

In addition to built-in HTML Elements, Markdown also supports a few custom components for convenience.

<Heading />

The Heading component renders all h1 through h6 elements. It receives the following props:

---
import { Markdown } from 'astro-remote';
import Heading from '../components/Heading.astro';
const content = await fetch('http://my-site.com/api/v1/post').then(res => res.text());
---

<!-- Render all <h1> through <h6> using custom <Heading> component -->
<Markdown content={content} components={{ Heading }} />

A sample Heading component might look something like this.

---
const { as: Component, href } = Astro.props;
---

<Component><a href={href}><slot /></a></Component>

<CodeBlock />

The CodeBlock component allows you customize the rendering of code blocks. It receives the following props:

A sample CodeBlock component might look something like this.

---
const { lang, code, ...props } = Astro.props;
const highlighted = await highlight(code, { lang });
---

<pre class={`language-${lang}`}><code set:html={highlighted} /></pre>

<CodeSpan />

The CodeSpan component allows you customize the rendering of inline code spans. It receives the following props:

A sample CodeSpan component might look something like this.

---
const { code } = Astro.props;
---

<code set:text={code} />

<Note />

The Note component allows you customize the rendering of GitHub-style notes and warnings. It receives the following props:

To use a Note component in Markdown, use the following syntax:

> **Note**
> Some tip here!

> **Warning**
> Some warning here!

Custom Components in Markdown

If you'd like to allow custom components in Markdown, you can do so using a combination of the sanitize and components options. By default, sanitization removes components.

Given the following markdown source:

# Hello world!

<MyCustomComponent a="1" b="2" c="3">It works!</MyCustomComponent>
---
import { Markdown } from 'astro-remote';
import MyCustomComponent from '../components/MyCustomComponent.astro';
const content = await fetch('http://my-site.com/api/v1/post').then(res => res.text());
---

<Markdown content={content} sanitize={{ allowComponents: true }} components={{ MyCustomComponent }} />

Using Marked Extensions

If you'd like to extend the underlying Marked behavior, the marked prop accepts extensions.

---
import { Markdown } from 'astro-remote';
import markedAlert from 'marked-alert'

const content = await fetch('http://my-site.com/api/v1/post').then(res => res.text());
---

<Markdown content={content} marked={{ extensions: [ markedAlert() ] }} />