RFC: Interleaving Markdown in JSX
See original GitHub issueSummary
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:
# Hello, <span>*world*</span>, from <>{props.name}</>!
<Note>
This *is* **markdown**!
</Note>
Output JSX
<MDXLayout
{...layoutProps}
{...props}
components={components}
mdxType="MDXLayout"
>
<h1>
{`Hello, `}
<span>
<em parentName="h1">{`world`}</em>
</span>
{`, from `}
<>{props.name}</>
{`!`}
</h1>
<Note mdxType="Note">
<p>
{`This `}
<em parentName="p">{`is`}</em>
{` `}
<strong parentName="p">{`markdown`}</strong>
{`!`}
</p>
</Note>
</MDXLayout>
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.
# Hello, from <>{props.name}</>
<Note>
Here's another expression: <>{props.thing}</>
</Note>
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:
# Hello, world!
<div>
<div>
# I should be an h1 inside two divs
</div>
<div>
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
# Hello, world
<SomeComponent>
{prop => {
const newValue = doStuff(prop)
return <h1>{newValue}</h1>
}}
</SomeComponent>
Template strings
# Hello, world!
<SomeComponent
someProp={`
Here's a template string
with empty lines
`}
/>
Alternatives
The other main alternative we’ve considered is an MDX
component.
# Hello, <span><MDX>*world*</MDX></span>!
<Note>
<MDX>
This *is* **markdown**!
</MDX>
</Note>
An additional alternative from PHP Markdown Extra is using a markdown attribute:
# Hello, <span markdown="1">*world*</span>!
<Note markdown="1">
This *is* **markdown**!
</Note>
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
Issue Analytics
- State:
- Created 4 years ago
- Reactions:67
- Comments:53 (23 by maintainers)
Top GitHub Comments
Heads up everyone: we’re working on rewriting the parser to solve amongst other things, this problem, in 2.0.0. Out relatively soon!
For future googlers who find this, it seems the newlines are important
This does not work for me
While this does