DEV Community

Cover image for Render Markdown from a string in Svelte (mdsvex / SvelteKit)
Will
Will

Posted on • Edited on

Render Markdown from a string in Svelte (mdsvex / SvelteKit)

The Svelte Markdown tutorials I've seen out there assume that you have access to a .md file or are writing Markdown directly in a preprocessed file.

What if you wanted to render a raw string that contains Markdown? Example use-case: You want the flexibility of being able to serve Markdown content from a CMS (such as Strapi) without having to make changes to a .md file.

There are a few ways you can achieve this, in this solution we'll be using mdsvex to convert the Markdown into HTML format, then rendering that HTML. mdsvex is a Markdown preprocessor which is essentially a wrapper for MDX, making it compatible for Svelte.

Installation

If you're not using SvelteKit, check out the MDsveX docs for alternative installations, if you are:

Start by installing it as a dev-dependency.



npm i --save-dev mdsvex


Enter fullscreen mode Exit fullscreen mode

or...



yarn add --dev mdsvex


Enter fullscreen mode Exit fullscreen mode

Then update the Svelte configuration to include an extensions array:

(We'll just be using a .svelte file, but you can also include extensions such as .svx if you plan on using those file types)

svelte.config.js



export default {
  preprocess: [
    // your existing config
  ],

  extensions: ['.svelte'], // Add this

  kit: {
    // your existing config...
  }
};


Enter fullscreen mode Exit fullscreen mode

That's all the configuration we need for now. ☑️

Passing the compiled string to the component

Let's set up a route loader that simulates fetching data from an API:

+page.server.ts



import type { Load } from '@sveltejs/kit';

const MOCK_RESPONSE_FROM_API = `
## Lorem

Lorem is currently extended with the following plugins.
Instructions on how to use them in your application are linked below.

| Plugin | README |
| ------ | ------ |
| Dropbox | [plugins/dropbox/README.md](Link) |
| Medium | [plugins/medium/README.md](Link) |
| Google Analytics | [plugins/googleanalytics/README.md](Link) |
`;

export const load: Load = async () => {
    const response = MOCK_RESPONSE_FROM_API; // Get data with eg. `fetch`

    return response;
};


Enter fullscreen mode Exit fullscreen mode

Next, we can import the compile function from mdsvex, declare a variable passing the (mock) response into it, and return it so it's available in our component. We'll also log it to see what gets returned from compile:

+page.server.ts



import { compile } from 'mdsvex';
// ...
const compiledResponse = await compile(response);

console.log('compiledResponse is: ', compiledResponse);

return compiledResponse;
// ...


Enter fullscreen mode Exit fullscreen mode

console output (Given this is being run server side, it's viewable on the terminal, not the browser console)

Console output from compile

As you can see, the Markdown has been compiled into what looks like HTML string format, appended together with the + operator, under the key of code.

So we can update the return statement to only include what we need:

+page.server.ts



export const load: Load = async () => {
    const res = MOCK_RESPONSE_FROM_API; // Get data with eg. `fetch`
    const compiledResponse = await compile(res);

    return { content: compiledResponse?.code };
};


Enter fullscreen mode Exit fullscreen mode

Rendering the HTML directly into the component

Now from our component, all we need to do is receive the data through export let, and render it.

Usually, strings are inserted as plain text, meaning that characters like < and > have no special meaning. So given we've now got HTML that we want to render directly, we can use HTML tags:



<p>{@html string}</p>


Enter fullscreen mode Exit fullscreen mode

so in our case...

+page.svelte



<script lang="ts">
    export let data = {
        content: ''
    };
</script>

<div>{@html data.content}</div>


Enter fullscreen mode Exit fullscreen mode

Which gives us our rendered Markdown 🪄:

Rendered Markdown

Note from the docs:

Svelte does not sanitise expressions before injecting HTML. If the data comes from an untrusted source, you must sanitise it, or you are exposing your users to an XSS vulnerability.

Further Customisation

The compile method accepts a second parameter, options:



interface MdsvexOptions {
    extensions: string[];
    smartypants: boolean | smartypantsOptions;
    layout: string | { [name: string]: string };
    remarkPlugins: Array<plugin> | Array<[plugin, plugin_options]>;
    rehypePlugins: Array<plugin> | Array<[plugin, plugin_options]>;
    highlight: { highlighter: Function, alias: { [alias]: lang } };
    frontmatter: { parse: Function; marker: string };
}


Enter fullscreen mode Exit fullscreen mode

This means that you can transform your HTML with tools like rehype or remark. You can find out more about the available options here.

That's it! 👨‍💻

If you need any further information on the above or anything else related - check out the SvelteKit docs, the MDX docs or the official Svelte discord.

Top comments (1)

Collapse
 
sashko profile image
Oleksandr

Okay, but how to use Svelte components in the MD to render them on a page?