MDX compiledContent()
support
#419
Replies: 11 comments 12 replies
-
another impl of RSS is https://www.jsonfeed.org/, which would be nice to support as well |
Beta Was this translation helpful? Give feedback.
-
Thanks for carrying this conversation forward and for mentioning my method. I do want to make it super clear it was Chris Adiante who came up with the method and it wouldn’t have occurred to me it was possible that way. It only works for static sites, but it’s his creative and clever thinking that made it happen. Just wanted to make sure he gets due credit. 😊 |
Beta Was this translation helpful? Give feedback.
-
Related discussion: #409 |
Beta Was this translation helpful? Give feedback.
-
I would love to see support for rendered HTML content from MDX files for full content in RSS! I'm loving the experimental collections API and thought I might be able to render out the content with the render() method available on the returned collection data, but found this RFC after hacking around for a while without success. I wish I had some experience here to help contribute, but for now, just wanted to +1 the idea. Thanks, Ben! |
Beta Was this translation helpful? Give feedback.
-
This disccussion is super positive. I've also seen methods to generate the rss feed using the content experimental feature, but for me is important that astro enables a simple approach that not require collections just to generate the feed. It would be great if we could use MDX files in the same way MD files are already supported in the rss example provided on the official guide (https://docs.astro.build/en/guides/rss/). |
Beta Was this translation helpful? Give feedback.
-
I was also using What needs to happen to move this forward? Happy to jump in with some guidance. |
Beta Was this translation helpful? Give feedback.
-
for those who don't want to join Discord just to get the info on Scott Willsey's workaround with |
Beta Was this translation helpful? Give feedback.
-
Is there a solution for this? |
Beta Was this translation helpful? Give feedback.
-
I have a tangential request: I have relatively complicated pages, rendered based on collections. They're not really complicated, they just have custom MDX components, some slots, etc. I don't think this method being discussed here is valid for my use case. Hopefully I'm not the only one ahah So my question is: could there be a way to "force" the RSS render to be the last thing to happen, and somehow have access to the final rendered HTML of the page? This would avoid repetition between stuff we do on the slugged pages and the RSS feed (right now we have to have the same rendering logic twice). We could then parse and transform the HTML on a case by case (for example, I would want to remove everything except the What do you think? :) |
Beta Was this translation helpful? Give feedback.
-
Sharing https://github.com/delucis/astro-blog-full-text-rss/ here as I saw some people mentioning RSS. Would still be nice to improve the DX here but that repo demonstrates rendering MDX files in an RSS feed with the container API. |
Beta Was this translation helpful? Give feedback.
-
Sharing my current solution here as well (Astro 5). The one from @delucis did not work for me because node complained about the imports. So this uses slightly different imports, which work for me. Also I use sanitize-html instead of ultrahtml as that one left in way too many tags and attributes and the filter options were cumbersome (no reasonable defaults). Besides that, it should be mostly the same. Some code and comments is also just straight up copied from there :-) import { getCollection, render } from "astro:content";
import { SITE } from "@/config";
import mdxRenderer from "@astrojs/mdx/server.js";
import reactRenderer from "@astrojs/react/server.js";
import rss, { type RSSFeedItem } from "@astrojs/rss";
import type { APIContext } from "astro";
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import sanitizeHtml from "sanitize-html";
export async function GET(context: APIContext) {
// Get the URL to prepend to relative site links. Based on `site` in `astro.config.mjs`.
let baseUrl = context.site?.href || "https://*****.de";
if (baseUrl.at(-1) === "/") baseUrl = baseUrl.slice(0, -1);
// Create a new Astro container that we can render components with.
// See https://docs.astro.build/en/reference/container-reference/
const container = await AstroContainer.create();
// Load MDX and React renderer.
// Other renderers for UI frameworks (e.g. Vue, etc.) would need adding here if you were using those.
container.addServerRenderer({
renderer: mdxRenderer,
});
container.addServerRenderer({
renderer: reactRenderer,
});
container.addClientRenderer({
name: "@astrojs/react",
entrypoint: "@astrojs/react/client.js",
});
// Load the published posts to add to our RSS feed.
const posts = (
await getCollection("posts", ({ data }) => Boolean(data.published))
).sort((a, b) =>
// Satisfy TypeScript by checking for `undefined` first.
!a.data.published || !b.data.published
? -1 // Sort by published date, descending.
: b.data.published > a.data.published
? 1
: -1,
);
// Loop over blog posts to create feed items for each, including full content.
const feedItems: RSSFeedItem[] = await Promise.all(
posts.map(async (post) => {
const feedItem: RSSFeedItem = {
title: post.data.title,
description: post.data.description,
pubDate: post.data.published,
link: `/posts/${post.id}/`,
};
// Get the `<Content/>` component for the current post.
const { Content } = await render(post);
// Use the Astro container to render the content to a string.
const postHtml = await container.renderToString(Content);
// Process and sanitize the raw content with sanitize-html.
// Also make sure that relative links are converted to absolute links.
const sanitizedHtml = sanitizeHtml(postHtml, {
allowedTags: [...sanitizeHtml.defaults.allowedTags, "img"],
transformTags: {
a: (tagName, attribs) => ({
tagName,
attribs: {
...attribs,
...(attribs.href && {
href: attribs.href.startsWith("/")
? baseUrl + attribs.href
: attribs.href,
}),
},
}),
img: (tagName, attribs) => ({
tagName,
attribs: {
...attribs,
...(attribs.src && {
src: attribs.src.startsWith("/")
? baseUrl + attribs.src
: attribs.src,
}),
...(attribs.href && {
href: attribs.href.startsWith("/")
? baseUrl + attribs.href
: attribs.href,
}),
},
}),
},
});
feedItem.content = sanitizedHtml;
return feedItem;
}),
);
return rss({
title: SITE.title,
description: SITE.description,
site: baseUrl,
items: feedItems,
customData: "<language>en-us</language>",
stylesheet: "/assets/styles/rss.xsl",
});
} |
Beta Was this translation helpful? Give feedback.
-
We've received multiple user requests for an MDX -> HTML string output (discord discussion, discord discussion, RSS PR thread). This is primarily for RSS feed support, where you often need the fully rendered content of a post to embed in your XML. MDX's use of a
<Content />
component makes this a no-go.This has led to some creative solutions in the community. Scott Willsey's is especially notable, as it uses Astro's
slots.render
as a hack to access the compiled HTML. This also proves it is possible to stringify MDX documents with some workarounds.Background
MDX is built on Astro's own JSX runtime. This means we rely on our own transformers to process MDX into
<JSXExpressions />
. This unlocks Astro-like features in MDX, including theclient:
directive, theset:html
directive, and support for any framework (React, Vue, Svelte, etc).This also makes Astro's MDX integration incompatible with other transformers, including React's
renderToString
helper to stringify a JSX tree. In order to support this, Astro needs to build its ownrenderToString
transform that can convert MDX output to an HTML string. The MDX compiler also offers an HTML string output, though it requires the unsafe use ofeval
, making it a no-go for Astro internally.Potential solutions
Update A
renderToString()
conversation is now being tracked at #462! 🥳1. Introduce a stringify utility to Astro's JSX runtime. There is ongoing work to expose an Astro-to-HTML utility that may be used for unit testing Astro components. Once this is complete, we can test the performance implications to see if a
compiledContent()
utility can be used.2. Allow
.xml
output from.astro
files. Scott Willsey's approach demonstrated that.astro
files can be used for templating XML, as long as the user usesset:html
to parse XML syntax as-needed. If Astro allows users to output.xml
, we bypass the need for an RSS endpoint entirely by using the existing<Content />
component to generate XML. This solution comes with some major drawbacks:set:html
may be intimidating to new users.compiledContent()
issue for RSS users. If others see a need forcompiledContent()
outside of RSS (ex. to output MDX from a JSON API), the issue still remains.Beta Was this translation helpful? Give feedback.
All reactions