-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
RFC: Interleaving Markdown in JSX #628
Comments
It's probably worth mentioning at the beginning what the output of span/world should be as fragments are mentioned later for interpolation but not for interleaving
|
What should happen in this case? # Hello, <>from {props.name}</> It feels to me that if this worked, this should work as well: # Hello from {props.name} These things are hard 😓 |
Yeahhh, good point. I wonder if we should only allow embedded expressions to be the only "children" in a JSX block (whether inline or block - |
Yeah, I think that would be a win-win because it wouldn't be syntactically misleading. |
would my use case in this issue: ...be solved by this kind of feature? I realized that I'm really wanting to wrap different lists in different styles, but still write the lists in markdown, but then include jsx in the markdown, like so: <ListStyleOne>
- Thing
- Subthing and this word is <Red>Red</Red> right here.
- Another subthing with a <Blue>colorful</Blue> word again.
</ListStyleOne> it seems like this is the case initially, but I just want to be sure that I'm understanding this proposal correctly XD |
It would 😄 |
This comment has been minimized.
This comment has been minimized.
This looks good! I think it makes sense for users. But there’s still some parser problems. Here goes a few more questions and random thoughts.
DrawbacksInteresting insights: MDX in JSX blocks in MDX is typically not indented, and needs two blank lines. I think that’s too fragile to depend on though. 🤔 |
Would it make the parsing more explicit if the JSX block included a |
Thanks for the thoughts @wooorm! I've addressed a few things in edits to the original comment.
Yeah I think this is something we'll need to also address since, you're right, it isn't 😆.
Thanks for pointing this out @rsexton404! I've added it as an alternative in the comment. It'd definitely make parsing easier due to it's explicitness, but it's also a bit noisy. I'll need to think on that one! |
Re # Hello, <span markdown="1">*world*</span>!
<Note mdx={true}>
This *is* **markdown**!
</Note> |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Thanks for the fast reply @wooorm. The first example you showed would work for a single JSX slot, but my component actually allows an arbitrary number of slots:
The only workaround I can think of that still allows for Markdown content is to make each slot a child component:
This works, and supports Markdown content (in MDX v2). 👍 It's just tedious and feels a lot more like writing XML than Markdown 😟 |
Great to hear that that works! Is this good enough for you? Or do you feel very strongly that more markdown should also be available in props? I think that that would be technically very hard. Although, maybe the MDX component you showed before could be implemented in userland, if that also works? |
I would expect any JSX component in an MDX file to support Markdown content, regardless of where the component occurs. Examples include:
Let me emphasize that while I think it would be totally awesome to support all of the above examples, I realize this means you'd basically have to write a JavaScript parser. 😰 So, no, I don't feel strongly that this is a must-have feature. MDX is already friggin' awesome, crazy powerful, and incredibly useful. |
Your last observation is indeed correct, to rephrase: we parse markdown and recognise the JSX inside it. Your expectation is the inverse: parse JavaScript and recognise markdown inside it. It sounds interesting, but to me it doesn’t align well with this project, or at least my ideas of how content should work, maybe others will have different opinions though! |
And thanks, glad to hear MDX is generally working well for you! Also: do let us know more about your experience w/ mdx@next! |
Hi y’all! I’m going to close this as it landed on the The reason is so that it’s more clear which issues still need to be solved already and don’t require further work. Expect another For more info, see #1041. |
Wonderful feature, I wanted to add a comment on my particular use case which is the same as JamesMessinger.
While it is pretty unrealistic to expect mdx to support markdown within JSX as this would be incredibly difficult, It does add an additional layer of consideration. As now the behavior is different dependent on whether a component is at the root layer / a direct child thereof, or whether a component is part of a property (e.g jsx) Consider the following two examples: <SomeComponent>
<>**Some *Markdown***</>
</SomeComponent>
<SomeComponent Content={<>**Some *Markdown***</>}/> The behavior differs between them which is something any Developer used to JSX/TSX is not going to expect. In Most/All Modern frameworks there is almost complete consistency between the HTML & JSX Approach of defining content which does not translate well to MDX. This is something that the alternate proposal does not have a problem with. As the content is clearly marked as mdx and as such can be parsed even within JSX code. <SomeComponent Content={<MDX>**Some *Markdown***</MDX>}/> I feel it would be worthwhile to pursue as not a replacement of of current system but rather an addition to. Giving a pretty consistent feel for any top level content while allowing devs to still utilize mdx within JSX. |
Thanks for sharing @CEbbinghaus!
There are other ways to support this. <SomeComponent Content={mdx`**Some *Markdown**`}/> <SomeComponent Content={<MDX>**Some *Markdown**</MDX>}/> as JSX handles spacing and newlines in a different and conflicting way from markdown |
I saw your comment recommending a template literal to pass into components1. However there is no existing template literal that will do this & the ones you can utilize require mdx at runtime as it won't be evaluated at compile time. I have nothing against the template literal approach although whichever approach taken it would be good to have official support for it and ensure it is consistent (e.g it builds at compile time). Additionally if template literals are the preferred method it would be good to integrate that into the LSP to allow for the same autocomplete behavior as when editing anything else. The Dev Experience is something I put a lot of value on and I would like nothing more than to make MDX feel as consistent and smooth as it can. Please do correct me if I am wrong about there being no built in template literal. I am happy to contribute 👍🏻 Footnotes |
You could write a recma plugin that finds |
@CEbbinghaus This is an older issue, about adding interleaving in general. I’d feel more comfortable discussing more and different interleaving methods in a new discussion, so as to keep issues focussed. Your idea is fundamentally very complex due to several things. One reason is that MDX also includes ESM and expressions. In those, entire functions and components can be defined, dynamically. export function SomeComponent() {
return <>Is this markdown?</>
}
{
(function () {
return <>Is this markdown?</>
})()
} The other part of why “interleaving in JS” is tough, is that it is unclear what “markdown content type” is expected. <X x={<>- Is this a list?</>} y={<>Is this a paragraph?</>} z={<>Is this *emphasis*, what about the outer paragraph?</>} /> Finally, JSX text also has certain handling that is present in Babel, Acorn, TypeScript, etc, which trims whitespace, also around line endings. Whereas whitespace is important to markdown. You’d have to either break from JS(X) or from markdown. Indeed, you can do things with plugins. |
Summary
Interleaving Markdown and JSX in MDX has been asked for by a lot of folks and is something we should officially support. It's been on the roadmap, but we've decided to add it as a minor version after v1. Now is that time 😸
It's a common need to wrap a block of content to achieve things like image + captions, notifs, callouts, and custom background/text colors for given sections.
Basic (proposed) example
Inline JSX blocks are automatically interleaved and block-level JSX requires an blank lines to open back up Markdown parsing:
Output JSX
Motivation
This approach is currently being used in the wild and in production. As @jxnblk mentioned previously this approach comes with the benefit that it's in the Commonmark Spec.
Detailed design
The majority of parsing is already implemented. However, there are a few things we need to be able to address.
Handling embedded expressions
{props.foo}
There are two additional scenarios where we need to be able to handle embedded expressions, which all essentially boil down to inline JSX.
This can get a bit tricky because technically the following should also be handled:
# Hello, <>from {props.name}</>
Handling more complex JSX blocks
When there is indented JSX across two lines the parser currently misses it. Something that doesn't currently work, but should:
Drawbacks
This will likely make the last JSX block parsing issue more difficult. The biggest remaining block issue is where we have empty lines in the JSX. This causes parsing breakages for render props and template strings.
Render props
Template strings
Alternatives
The other main alternative we've considered is an
MDX
component.An additional alternative from PHP Markdown Extra is using a markdown attribute:
This comes with the benefit of being more explicit, but it is confusing for folks coming from traditional Markdown as they slowly adopt JSX. It also feels syntactically noisy.
I've toyed with different syntax in place of
MDX
like aliasing it to something terser:<_>
. However, this isn't as approachable.Adoption strategy
Considering this already mostly works and is already being used (just not officially supported) the adoption strategy should be straightforward. We'd need to fix some parsing edge cases mentioned above and add documentation.
Related issues
cc/ @wooorm @jxnblk @ChristopherBiscardi @timneutkens @ChristianMurphy @silvenon
The text was updated successfully, but these errors were encountered: