What I'm trying to do

A simple way to render the content of a markdown file when it's passed as a string to another component using .compiledContent (or even using .rawContnent)? Or even a better way than this as obviously usually in Astro we can use the <Content /> Component, but from my knowledge, I can't pass a component or this functionality to another component without using a <slot /> in the parent component.

I have some JS for the parent component and using a <slot/> instead of passing the props to the component would change things, so hopefully looking for a solution with using this.

My setup

  • Data stored in /src/data/experience as markdown files with a year and a description formatted as markdown in the content section of each file
  • A component called Tabs.astro which takes props of headings and contents which are both lists of strings
  • A page /src/pages/experience.astro with the Tabs component in it which is displaying this data

I take the below code to get the data from the markdown files and pass the years and descriptions to the Tab component.

experience.astro

--- import Tabs from "../components/Tabs.astro"; const jobs = await Astro.glob("../data/experience/*.md"); const years = jobs.map((job) => job.frontmatter.year); const descriptions = jobs.map((job) => job.compiledContent); --- <!-- My component taking the data to be rendered --> <Tabs headings={years} contents={descriptions} /> 

Tabs.astro

And the component renders the info like so

<!-- Tabs --> <div> <ul> { headings.map((heading) => ( <li>{heading}</li> )) } </ul> <ul> {contents.map((content) => <li>{content}</li>)} </ul> </div> 

My current solution

At the moment using .compiledContent gets me the correct HTML, however it is all in a string so the HTML doesn't actually render.

What I'm looking for

  • Is there a native way in Astro to pass markdown as a prop to a component?
  • If not is there a manual and recommended way in Astro to convert a markdown string and sanitise it to protect against XSS attacks? (if this is a risk in Astro when rendered statically?)
  • If not what are your most recommended ways to render markdown and sanitise it in JS?

Thanks so much for your time and help! I'm loving using Astro

p.s Also happy to concede and just use a <slot/> in my component if needed... ;)

3 Answers

Astro has a set:html directive you can use in combination with a Fragment like this

<Fragment set:html={post.compiledContent()}/> 
2

After a bit of struggling with this myself, the current solution from the Astro docs for a single file without looping is the following. Import your file with {Content as YourAliasName} from '../yourPath/yourFileName.md' Then just use it as a tag <YourAliasName />

Example from the docs for reference:

--- import {Content as PromoBanner} from '../components/promoBanner.md'; --- <PromoBanner /> 

1

Markdown passed as a json structure prop

Please note that this is a little stretch from the original request, but I believe fulfill it in exactly the intended way.

What is Markdown AST ?

Makrdown remark is the conventional way to extract an AST (Abstract Syntax Tree) from Markdown which is compatible with standard flavors e.g. Github Flavored Markdown

const processor = remark() .use(remarkDirective) .use(remarkGfm) const markdownAST = processor.parse(content); 

How to render an AST with Astro

This is a tricky one but not that complex. In Astro there is possibility to implement a recursive component by using Astro.self . A recursive renderer looks like this

const {node, data} = Astro.props; const handled_types = [ "root","heading","table","paragraph", "image","code","link","list", "listItem" ] const other_type = !handled_types.includes(node.type) --- {(node.type == "root") && <> {node.children.map((node)=>( <Astro.self node={node} data={data} /> ))} </> } {(node.type == "paragraph") && <div> {node.children.map((node)=>( <Astro.self node={node} data={data}/> ))} </div> } {(node.type == "list") && <ul> {node.children.map((node)=>( <Astro.self node={node} data={data}/> ))} </ul> } {(node.type == "listItem") && <li> {node.children.map((node)=>( <Astro.self node={node} data={data}/> ))} </li> } {(node.type == "heading") && <Heading node={node} headings={data.headings}/> } {(node.type == "table") && <DataTable node={node} /> } {(node.type == "image") && <MarkdownImage node={node} filepath={data.path}/> } {(node.type == "code") && <Code node={node} filepath={data.path}/> } {(node.type == "link") && <Link node={node} filepath={data.path}/> } {other_type && <Fragment set:html={toHtml(toHast(node))}></Fragment> } 

see also a similar question "How to customize markdown with Astro components?" where I first solved this rendering issue.

References

I did create a readily implemented solution of the above theory, it is also split in two parts, the first is independent from Astro and turns Markdown into a content management system database, the second render it like you would render data from any headless CMS.

  • Markdown to AST and content list
  • example rendering Markdown AST with Astro components where Markdown is passed as json tree prop

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.