Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[md/mdx] components export for mdx in integration guide #1234

Closed
kylebutts opened this issue Aug 8, 2022 · 15 comments
Closed

[md/mdx] components export for mdx in integration guide #1234

kylebutts opened this issue Aug 8, 2022 · 15 comments
Labels
add new content Document something that is not in docs. May require testing, confirmation, or affect other pages.

Comments

@kylebutts
Copy link
Contributor

One thing I really like about MDX is that you can pass custom components for default html elements (h1/6, img, a, code, pre, etc.) with export const components = {}. The names comes from the Table of Components for MDX. Much easier than writing remark/rehype plugins IMO.

There's an example of it here, but could be nice to highlight in the docs: https://stackblitz.com/github/withastro/astro/tree/latest/examples/with-mdx?file=src%2Fpages%2Findex.mdx

@bholmesdev, Would you welcome an additional section in the MDX integration guide? I was told that you were working on those docs, so I don't want to interfere or do work you may already have done!

@kylebutts kylebutts changed the title [md/mdx] [md/mdx] components export for mdx in integration guide Aug 8, 2022
@yanthomasdev yanthomasdev added the add new content Document something that is not in docs. May require testing, confirmation, or affect other pages. label Aug 8, 2022
@delucis
Copy link
Member

delucis commented Aug 12, 2022

Adding to this, you can also do this after importing MDX:

---
import Content from '../content.mdx';
import Heading from '../Heading.astro';
---

<Content components={{ h1: Heading }} />

This is particularly useful in a dynamic route where you can glob all your .mdx content and render each to its own page while passing in default replacement components. (This is how I handled custom image components on the new astro.build site.)

---
// src/pages/[slug].astro
import path from "node:path";
import Layout from "../layouts/Layout.astro";
import Heading from "../components/Heading.astro";

export async function getStaticPaths() {
  const posts = await Astro.glob('../content/*.mdx');
  return posts.map(post => ({
    params: { slug: path.parse(post.file).name },
    props: post,
  }));
}
---

<Layout>
  <Astro.props.default components={{ h1: Heading }} />
</Layout>

@sarah11918
Copy link
Member

Thanks @kylebutts, @delucis! This sounds useful, and I'd absolutely want @bholmesdev's input on this.

We are actively (continuously? 😅) working on our MD/MDX documentation right now, including some reorganizing of content, so there is stuff happening all the time, and yes, I think adding halpful patterns is right up our alley at the moment!

@bholmesdev
Copy link
Contributor

@sarah11918 I totally agree we should document this, especially @delucis' components-as-props snippet above. We've gotten enough support questions on component rendering that warrants some docs. This could slide into the integration README for now, but I know we've talked about "proper" MDX docs separate from the README too. Worth aligning there.

@kylebutts
Copy link
Contributor Author

Want me to add a draft gist @sarah11918?

@sarah11918
Copy link
Member

@kylebutts That would be great! Do your worst! Thanks for offering. 🧑‍🚀

@kylebutts
Copy link
Contributor Author

I'm not sure where this would go, maybe after Layouts in the mdx integration?

Custom components

Under the hood, MDX will convert markdown into html components. For example,

> A blockquote with *some* emphasis.

will be converted into

<blockquote>
  <p>A blockquote with <em>some</em> emphasis.</p>
</blockquote>

MDX provides a way to tap into this rendering and use your own custom components. In the above example, you could create a custom Blockquote component (in any language) that has either a <slot /> component or accepts a children prop. Then in the MDX file you import the component and export it to the components export.

import Blockquote from '../components/Blockquote.astro';
export const components = { blockquote: Blockquote };

The advantage of this is it allows for the simplicity of writing in markdown without having to write the custom component or writing a remark/rehype plugin. A full list of components that can have custom components is on the [MDX website]https://mdxjs.com/table-of-components/).

Custom components with imported mdx

Custom components can also be passed to the components prop when rending imported MDX content.

---
import Content from '../content.mdx';
import Heading from '../Heading.astro';
---

<Content components={{ h1: Heading }} />

This is particularly useful in a dynamic route where you can glob all your .mdx content and render each to its own page while passing in default replacement components.

---
// src/pages/[slug].astro
import path from "node:path";
import Layout from "../layouts/Layout.astro";
import Heading from "../components/Heading.astro";

export async function getStaticPaths() {
  const posts = await Astro.glob('../content/*.mdx');
  return posts.map(post => ({
    params: { slug: path.parse(post.file).name },
    props: post,
  }));
}
---

<Layout>
  <Astro.props.default components={{ h1: Heading }} />
</Layout>

@bholmesdev
Copy link
Contributor

@kylebutts Couldn't have written this section better myself 🤩 My only critique is the "This is particularly useful in a dynamic route..." concluding section feels pretty advanced. I might want to keep that part out, and put the rest just after the "layouts" section as you suggested. Happy to review if you submit a PR for this!

@sarah11918
Copy link
Member

(Reminder to self that this has been PR'd in withastro/astro!)

@delucis
Copy link
Member

delucis commented Sep 3, 2022

Closing as withastro/astro#4530 has been merged 🥳

@delucis delucis closed this as completed Sep 3, 2022
@jtomek
Copy link

jtomek commented Sep 3, 2022

Thank you for the documentation.

I wondered if there is a way how to set the component overrides globally?

I would like to automatically replace <img /> tags with <Image /> in my mdx files.

@sarah11918
Copy link
Member

Hi @jtomek! The best place to ask this is in our Discord: https://astro.build/chat

If there isn't documentation that answers your question on this page: https://docs.astro.build/en/guides/integrations-guide/mdx/ or in MDX's documentation itself, then asking in our support-threads channel is the best place to have this discussion!

@jtomek
Copy link

jtomek commented Sep 3, 2022

Hello @sarah11918 , thank you. I will ask there.

@delucis
Copy link
Member

delucis commented Sep 3, 2022

I wondered if there is a way how to set the component overrides globally?

@jtomek I think the closest we have at the moment is what I describe above in my comment — importing all your MDX files and then rendering them by passing in the component overrides.

But as @sarah11918 says, Discord would be a great way to ask others and chat about solutions. Or otherwise a discussion in https://github.com/withastro/rfcs would be the place to start thinking of ways to support this.

@kylebutts
Copy link
Contributor Author

I think it would be a natural extension to include custom MDX components in the layout component. I can make an RFC for this if folks agree?

@delucis
Copy link
Member

delucis commented Sep 3, 2022

Definitely worth at least a discussion in the RFCs repo!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
add new content Document something that is not in docs. May require testing, confirmation, or affect other pages.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants