withastro / astro

The web framework for content-driven websites. ⭐️ Star to support our work!
https://astro.build
Other
44.73k stars 2.33k forks source link

Scripts and Styles in components included inside mdx content, loaded through another component, don't work #7761

Open trafnar opened 1 year ago

trafnar commented 1 year ago

What version of astro are you using?

2.9.1

Are you using an SSR adapter? If so, which one?

No

What package manager are you using?

npm

What operating system are you using?

Mac

What browser are you using?

Chrome

Describe the Bug

I'm looping through a content collection, using a component to render each content item. The content items themselves are defined in .mdx files, each with a component inside the mdx. That component does not get styling/scripts applied.

Reproducible example Stackblitz project

Basic Example of what's in the linked project

index.astro (some imports removed for brevity) ```astro --- const posts = (await getCollection("posts")); --- {posts.map((post) => )} ``` components/Post.astro ```astro --- const { post } = Astro.props; const { Content } = await post.render(); ---

{post.data.title}

``` components/Card.astro ```astro
This is a card
``` content/posts/my-post.mdx ```mdx --- title: "My Post" --- import Card from '../../components/Card.astro'; ``` The `` used in `my-post.mdx` will not have styling applied

What's the expected result?

The <Card /> component should have it's local styles applied.

Link to Minimal Reproducible Example

https://stackblitz.com/edit/github-u2yta7?file=src%2Fpages%2Findex.astro

Participation

lilnasy commented 1 year ago

Thank you for the clean reproduction, it was really helpful!

I'll let one of the other maintainers confirm, but this seems to be an architectural limitation.

Due to Astro's streaming, we only wait until the page's frontmatter has run before generating the <head> element. Frontmatter of a component (in your case, components/Post.astro) cannot add scripts or stylesheets because by the time it's run, the <head> is already sent to the browser so that it can start loading assets.

In case of SSG, it shouldn't matter, but the same architecture is used for both modes.

ChrisPlease commented 1 year ago

Just started exploring Astro yesterday and ran into this today. My component is a 1 off, so as a work around I set <style is:inline>, but I can see how that's not useful in a loop.

kurtextrem commented 11 months ago

If anyone finds this issue: I've worked around it by including the component in an Astro layout (.astro) (wrapped in a hidden div), so the price we pay is duplicate HTML output (1x in the MDX, 1x in the layout), but assets (CSS & JS) are only added once.

I still think this is a kind of drastic limitation and should definitely be documented somewhere.

natemoo-re commented 11 months ago

Duplicate issues

Closing those to keep the conversation going here.

natemoo-re commented 11 months ago

Thanks for investigating @lilnasy! I'm not convinced this is an architectural limitation—it might just be a bug with how we crawl the module graph for styles.

There's currently some effort to improve that, but we had to move it behind an experimental flag for the time being. See some of the discussion in https://github.com/withastro/astro/issues/7857

Edit: unfortunately setting experimental: { optimizeHoistedScript: true } doesn't fix this issue, but the issue is definitely related to that algorithm.

trafnar commented 11 months ago

I tried using Imba code within Astro, using the Imba Vite plugin. I created an Imba component, wrapped it in an Astro component, then included that in an MDX file that was part of a content collection. This worked but the styles only appeared in development, not in production.

I think this is a similar issue, but its interesting that it works in development mode.

I have a reproduction of this which I was going to share on StackBlitz, but the Imba Vite plugin requires a newer version of Node than StackBlitz supports

lilnasy commented 11 months ago

@trafnar You can add codesandbox or a github repo in the reproduction.

trafnar commented 11 months ago

@lilnasy good idea. I believe this issue is actually distinct so I've opened an issue with a reproducible example as a github repo #8436

haikyuu commented 11 months ago

@trafnar In development, styles are loaded using an import like so import "/src/imba-component.imba?imba&type=style&lang.css";. It's because we're defaulting to emitCss = true. So it's working for both content collections and pages. (I think #8436 can be closed)

But in the build, styles are NOT included in content collection components. Stackblitz link to reproduce

image

whereas they are included in regular pages

image

So, from here it seems like Astro forgets to add the link tag in content collections 🤔

matthewp commented 11 months ago

This might be the same cause as https://github.com/withastro/astro/issues/8399. I'm working on that one now.

trafnar commented 11 months ago

@matthewp can't tell you how much I appreciate you working on this issue ❤️

lilnasy commented 10 months ago

Wrote up a proposal based on @matthewp's idea: https://github.com/withastro/roadmap/discussions/707.

lilnasy commented 8 months ago

I have published an integration that fixes this issue. GitHub | NPM

I have tested it to be working against the original repro provided by trafnar (https://stackblitz.com/edit/github-u2yta7?file=src%2Fpages%2Findex.astro)

matthewp commented 2 months ago

I think this is more of a limitation of how Content Collections works and probably not something that is fixable. However this feature might make this possible: https://caniuse.com/mdn-html_elements_link_blocking

Trombach commented 2 months ago

Hi @lilnasy, I had been using your integration (not directly but I copied the code file, since svelte-check had an issue with the integration for some reason 🤔 ), thanks for that btw! I believe the workaround is not working anymore since the MDX 3.0 integration was released :(

jcayzac commented 1 week ago

I just came to say I'm using SSG and I do see this issue.

jcayzac commented 1 week ago

As a workaround, I include all the components available to .mdx pages in my layout, wrapped by a <template> element. This has Astro include the styles in <head> when building.

Edit: Unfortunately it's not a complete workaround. The component I'm using in turn uses an Icon component from astro-icon, and that one is broken (styles are OK but no icon showing).

kyleshevlin commented 3 days ago

I have found a workaround that works for me, but I know won't work for everyone.

In the file where you call entry.render() to get the Content component, pass in the components that are giving you trouble in to the components prop on Content. I'll use my blog's code for example:

---
import TroublesomeComponent from './components/TroublesomeComponent.astro'

// other stuff

const { Content: PostContent } = await entry.render()
---

<PostContent components={{ TroublesomeComponent }} />

This now makes TroublesomeComponent globally available to any PostContent I write, which then adds the script and styles to the <head>.

Again, this might not be ideal, but I can live with this workaround and thought it might help others.