withastro / roadmap

Ideas, suggestions, and formal RFC proposals for the Astro project.
290 stars 29 forks source link

Container APIs #916

Open ematipico opened 3 months ago

ematipico commented 3 months ago

Summary

An API for rendering components in isolation:

// Card.test.js
import Card from "../src/components/Card.astro"
import astroConfig from "../src/astro.config.mjs";

const container = await AstroContainer.create()
const response = await container.renderToString(Card);
// assertions

Links

tordans commented 3 months ago

Hey! Can I use the new container API to render const { Content } = await page.render() into a string, pass this string to a React Component and render it there with dangerouslySetInnerHTML? I am looking for a way to render all posts in a blog post index kind of situation (Example) and pass them over to React without loosing all the markdown rendering that Astro does in <Content/>. It sounds like this API might help, but Content is not a component but returned by await page.render() so how does that work?

I tried passing Content but that fails:

const rawPages = await getCollection('posts')
const sortedPages = rawPages.sort((a, b) => a.data.order - b.data.order)
const pages: RadnetzPage[] = []
for (const page of sortedPages) {
  const { Content } = await page.render()
  const container = await AstroContainer.create()
  const result = await container.renderToString(Content)
  pages.push({
    slug: page.slug,
    menu: page.data.menu,
    order: page.data.order,
    title: page.data.title,
    links: page.data.links,
    Content: Content,
    contentHtml: result,
  })
}

Results in…

Unhandled rejection
Astro detected an unhandled rejection. Here's the stack trace:
NoMatchingRenderer: Unable to render Content.

No valid renderer was found for this file extension.
    at renderFrameworkComponent (/…/node_modules/astro/dist/runtime/server/render/component.js:190:15)
    at async Module.renderComponent (…/node_modules/astro/dist/runtime/server/render/component.js:396:10)

(I was directed here from the changelog.)

ematipico commented 3 months ago

@tordans yes you can, however you'll have to wait for this PR to be merged and released: https://github.com/withastro/astro/pull/11141

tordans commented 3 months ago

@tordans yes you can, however you'll have to wait for this PR to be merged and released: withastro/astro#11141

Perfect, thanks @ematipico. I noticed that https://github.com/withastro/astro/pull/11141 does not mention any Docs changes (yet) and https://github.com/withastro/docs/pulls?q=is%3Apr+is%3Aopen+container-reference doesn't either (yet). Given that I noticed a lack of docs on the topic of rendering lists – see https://github.com/withastro/docs/pull/8364 – that might be something to show in the docs for this API as well.

universse commented 1 month ago

Can I use this in a Cloudflare project? It currently throws an error during build.

[commonjs--resolver] [plugin vite:resolve] Cannot bundle Node.js built-in "node:fs/promises" imported from "..\..\node_modules\.pnpm\@astrojs+mdx@3.1.3_astro@4.12.2_@types+node@20.14.12_typescript@5.5.4_\node_modules\@astrojs\mdx\dist\index.js". Consider disabling ssr.noExternal or remove the built-in dependency.

I am using the MDX renderer specifically to render <Content /> from content collection.

ematipico commented 1 month ago

@universse it's hard to tell to give you an answer with this little information. Please use Discord.

universse commented 1 month ago
const { Content } = await entry.render()
const descriptionHtml = await astroContainer.renderToString(Content)

I'm using Astro container to render <Content /> to string and pass it to React component. I'm using @astrojs/cloudflare integration and run into the above build error. If I remove Astro Container code, the build completes. Same when I switch to @astrojs/node. So I think there's some bundling issue for Cloudflare worker.

Update: adding these to Astro config works for me.

{
  vite: {
    ssr: {
      external: ['astro/container', '@astrojs/mdx'],
    },
  },
}

Update 2: when deployed, only pre-rendered page works. server-rendered page will give status 500.