docs icon indicating copy to clipboard operation
docs copied to clipboard

How to render remote markdown using content layer API

Open johnmw opened this issue 1 year ago • 3 comments

📚 Subject area/topic

markdown, content layer api

📋 Suggested page

https://docs.astro.build/en/reference/configuration-reference/#experimentalcontentlayer

📋 General description or bullet points (if proposing new content)

I think that with the new content layer API, rendering remote markdown (from a server or api) is going to be a common use case.

The Astro docs currently says "Astro does not include built-in support for remote Markdown outside of experimental content collections!"

However I was unable to find any further information or examples about rendering remote (server) markdown.

So firstly, I was wondering if there already is an good example that I have missed?

If not, I have got some rough code (shown below) that renders markdown using Astros built in functions that seems to work and might help someone. But I would really like someone to review it and let me know things that can be improved.

Example Loader that renders remote markdown:

import { defineCollection, z } from 'astro:content';
import type { Loader } from "astro/loaders";

function markdownTestLoader({}):Loader {
  return {
    name: "markdown-test-loader",
    load: async ({ config, store, parseData, generateDigest, entryTypes }) => {
      
      store.clear();
      
      // Would normally load this JSON data from API. 
      // The "content" field has the markdown I want to render
      const dummyAPIData = {
        id: "home",
        title: "Test Markdown Data",
        content: "## Markdown to Render \nTesting markdown rendering.\n- List item 1\n- List item 2"
      }
      
      const id = dummyAPIData.id;
      const data = await parseData({ id, data:dummyAPIData });
      const body = dummyAPIData.content; // markdown to render next
      const digest = generateDigest(data);

      // Render markdown to HTML using built in Astro markdown render function
      let rendered;
      const markdownEntryType = entryTypes.get(".md");
      if( markdownEntryType?.getRenderFunction ) {
        const render = await markdownEntryType.getRenderFunction(config);
        rendered = await render?.({
          id,
          data,
          body,
          digest
        });
      }

      store.set({ id, data, rendered, digest });
    }
  }
}

// Define collection with loader

const markdownTestCollection = defineCollection({
  loader: markdownTestLoader({}),
  schema: z.object({
    id: z.string(),
    title: z.string(),
    content: z.string()
  })
});

export const collections = {
  'markdownTestCollection': markdownTestCollection  
};

The markdown HTML can be displayed on the page using the "render" function and built in "Content" component:

---
import { getCollection, render } from 'astro:content';

export async function getStaticPaths() {
  const pages = await getCollection("markdownTestCollection");
  return pages.map((page) => ({    
    params: { 
      slug: page.id },
    props: { 
      page 
    },
  }));
}

const { page } = Astro.props;
const { Content, headings } = await render(page);
---
<h1>{page.data.title}</h1>
<Content />

As I said, I'm sure there are things above that could be done better.

🖥️ Reproduction of code samples in StackBlitz

No response

johnmw avatar Oct 03 '24 01:10 johnmw