#How do you modify mdx content prior to rendering <Content />?

8 messages · Page 1 of 1 (latest)

hushed halo
#

Scenario: On my static website, I want to implement the Read More Link functionality for my list pages for posts that indicate a split in content.

In my pages listing a series of posts via pagination, I want to show a "Read More" link in some entries, like how Wordpress's Read More Block works. For certain posts, the content of a post needs to be split. The portion of the content after the Read More indicator needs to be removed and replaced with a link to the full page saying "Read more..." or "Continue reading...". The Read More indicator might be an HTML comment or some other text construction placed in the content text of the post itself. This indicator should not affect the output of the content on the full post page, only the list pages.

I do not see a way to modify the MDX text (or other types) prior to calling entry.render(), which creates the <Content /> component that is then placed in the rendered output of the list page. Modifying entry.body prior to entry.render() does not do anything, and probably rightly so, as it appears to just be for our reference.

How can one either control the text that goes into the entry.render() function, or alternatively, how can we modify the output of <Content /> once it is rendered?

sour quail
#

The way I know (and don't like) is querying the file's collection for the file's id and getting the raw markdown that way. Then you can manipulate it and render it (f.e. with unify). The reasons I don't like it are that the querying is convoluted and you have to make sure the rendering with unify or another parser is consistent with entry.render() and AFAIK you can't render MDX that way.

hushed halo
hushed halo
#

I think I've found a solution for this particular use case. Instead of having a <ReadMore /> component that sits in the middle of the content, you instead can have a <ReadMore> component that surrounds the secondary content that you want to conditionally hide. The component would have this form:

---
const { url } = Astro.props;
---
{url ? (
    <a href={url}>Read More...</a>
) : (
    <slot />
)}
#

As you can see, if a read more url is defined, it shows the link, otherwise, it renders the content of the <ReadMore> tag as normal.

#

Your MDX file might look like this:

---
title: "Read More Example"
date: '2024-09-16T01:50:47Z'
---
import ReadMore from 'src/components/ReadMore.astro';

Primary content

<ReadMore url={props.readMoreUrl}>
    Secondary content
</ReadMore>
#

Lastly, your list view page ([...posts].astro) would be something like this:

---
export async function getStaticPaths() {
    // Generate the list of posts such that one param is "entries" containing the subset of posts for this list page.
}

const post = Astro.props;

const entries = await Promise.all(post.entries.map(async (entry) => {
    const rendered = await entry.render();

    return {
        ...entry,
        hasReadMore: entry.body.includes('<ReadMore'),
        rendered: rendered,
    };
}));
---
<h1>Posts</h1>
{entries.map((entry) => (
    <h2><a href={`/${entry.slug}/`}>{entry.data.title}</a></h2>
    <entry.rendered.Content readMoreUrl={entry.hasReadMore ? `/${entry.slug}/` : undefined} />
))}
#

Once I actually launch the site, perhaps I'll make a post about this. For now, I'm going to call this solved.