In the previous article, I covered the why and how to use a Markdown component in Astro.

Here, we’re going to expand on that and help you use Markdown everywhere — regardless of the framework you use. So, this works for React, Vue, and Svelte.

The entire process hinges on the Markdown utility I’ve built for Splendid Labz.

Why This Utility?

I hit a snag when using most Markdown libraries. I naturally write Markdown content like this:

<div>
  <div>
    <!-- prettier-ignore -->
    <Markdown>
      This is a paragraph

      This is a second paragraph
    </Markdown>
  </div>
</div>

But since most Markdown libraries don’t account for whitespace indentation, they create an output with <pre> and <code> tags.

This is because Markdown treats indentation beyond four spaces as a code block:

<div>
  <div>
    <pre><code>  This is a paragraph

      This is a second paragraph
    </code></pre>
  </div>
</div>

So you’re forced to strip all indentation and write it like this instead:

<div>
  <div>
    <!-- prettier-ignore -->
    <Markdown>
  This is a paragraph

  This is a second paragraph
    </Markdown>
  </div>
</div>

That’s hard to read and annoying to maintain.

My Markdown utility handles this whitespace issue and generates the correct HTML regardless of how your code is indented:

<div>
  <div>
    <p>This is a paragraph</p>
    <p>This is a second paragraph</p>
  </div>
</div>

Using This in Your Framework

You have to pass the Markdown text into the utility. If inline is true, then markdown will return an output without paragraph tags.

Here’s an example with Astro:

---
import { markdown } from '@splendidlabz/utils'
const { inline = false, content } = Astro.props
const slotContent = await Astro.slots.render('default')

// Process content
const html = markdown(content || slotContent, { inline })
---

<Fragment set:html={html} />

You can then use it like this:

<Markdown>
   <!-- Your content here -->
</Markdown>

Here’s an example for Svelte. Note that Svelte cannot read dynamic content from slots, so we can only pass content through a prop:

<script>
  import { markdown } from '@splendidlabz/utils'
  const { content, inline = false } = $props()
  const html = markdown(content, { inline })
</script>

<!-- eslint-disable-next-line svelte/no-at-html-tags -->
{@html html}

And you can use it like this:

<Markdown content=`
  ### This is a header

  This is a paragraph
`/>

Building equivalent components for React and Vue follows the same pattern, so the implementation is left as an exercise for those frameworks.

Taking it Further

The Markdown utility is part of Splendid Utils, a collection of libraries consolidated from common web development tasks — along with additional packages for layouts and Astro and Svelte components. Together, they aim to reduce repetitive work and improve developer experience across projects.