Mdx: Can't access props with inline JSX

Created on 27 Apr 2019  路  7Comments  路  Source: mdx-js/mdx

With the new inline parsing of JSX blocks we no longer allow props access like we did in v0:

This is some inline JSX: <span>{props.foo}</span>

We now result in the following AST:

{
  "type": "root",
  "children": [
    {
      "type": "paragraph",
      "children": [
        {
          "type": "text",
          "value": "This is some inline JSX: "
        },
        {
          "type": "jsx",
          "value": "<span>"
        }
        {
          "type": "text",
          "value": "{props.foo}",
        },
        {
          "type": "jsx",
          "value": "</span>"
        }
      }
    }
  ]
}

This is problematic because "{props.foo}" will be escaped and end up like:

<span>{`{props.foo}`}</span>

Potential solution

Rather than parsing open/close blocks separately we should probably return a single node:

{
  type: 'jsx',
  value: '<span>{props.foo}</span>`
}

However, this can be problematic if folks are using Markdown inside inline JSX because their embedded Markdown will suddenly no longer work. This is also why remark parses inline HTML in that way. In the context of MDX, though, I'm not sure this is correct and we want it as a single JSX node.

cc/ @ChristopherBiscardi @wooorm @timneutkens @jxnblk @jlengstorf


AST Explorer example

馃悰 typbug

All 7 comments

Yeah I can see how people would expect markdown to work inside inline JSX.

Alternatively, could we look at and detect the braces?

Alternatively, could we look at and detect the braces?

Yeah, this is prolly the most reasonable approach. Then we could avoid string escaping those braces so something like:

<span>Hi, {props.foo}</span>

Would become:

<span>{`Hi, `}{props.foo}</span>

After some more thinking on this we might need to change the AST structure for inline JSX a bit. So each inline JSX tag has children so that we can parse the Markdown and nest inline JSX tags in a way that is unambiguous:

{
  "type": "root",
  "children": [
    {
      "type": "paragraph",
      "children": [
        {
          "type": "text",
          "value": "This is some inline JSX: "
        },
        {
          "type": "jsx",
          "children": [
            {
              "type": "jsx",
              "value": "<span>"
            },
            {
              "type": "emphasis",
              "children": [
                {
                  "type": "text",
                  "value": "Hi"
                }
              ]
            },
            {
              "type": "jsx",
              "value": "{props.foo}"
            },
            {
              "type": "jsx",
              "value": "</span>",
            }
          ]
        }
      ]
    }
  ]
}

I think I've run into this issue, but rather than pass any props with braces I just wanted to wrap markdown content within a JSX element.

At first this wasn't an issue, the content I was wrapping was plain text for the first line, then further paragraphs that used markdown syntax rendered correctly. I had an inline JSX element where the markdown just seemed to return as a string instead of being properly converted..

import InstallPath from './embeds/install-path.mdx'

In the Rust development environment, all tools are installed to the <InstallPath/> directory,
import PlatformSpecific from '../../components/detect-platform'

<PlatformSpecific unix>`/.cargo/bin`</PlatformSpecific>
<PlatformSpecific win>`%USERPROFILE%\.cargo\bin`</PlatformSpecific>

Which would output:

In the Rust development environment, all tools are installed to the `/.cargo/bin` directory,

Instead of:

In the Rust development environment, all tools are installed to the /.cargo/bin directory,

Turned out the content would not render as markdown until after the first blank line:

<PlatformSpecific unix>

`/.cargo/bin`
</PlatformSpecific>

Which works, but forces a new line in the actual content too breaking that sentence. Removing the blank line above it renders all as a string like when I originally had it all on a single line. Any content before that first blank line can be plain text or JS/JSX I take it.

Is that the intended behaviour?

The suggested brace approach works I guess, would that allow for such, as using a variable that gets replaced in code fences?(or my example if I passed a prop in with a string to be rendered as an InlineCode element, rather than creating a component specifically for it)

Note a blank line isn't required afaik if you start with heading content #

The content being treated as a newline/block was due to implicitly generated <p> tag wrapping the MDX content by default. AFAIK, I just have to opt-out of that by changing the wrapper to a fragment if possible. Sometimes it would be desirable at least when importing MDX files to also have the implicit paragraph tag wrapping the content, is there a way to opt-in/out?

For anyone else struggling with this, one workaround is to wrap the line of text in React.Fragment or span:

<React.Fragment>This is some inline JSX: {props.foo}</React.Fragment>

Note: Markdown needs to be used _outside_ of JSX, otherwise the content isn't parsed by MDX

e.g.

<React.Fragment> > This is some inline JSX: {props.foo}</React.Fragment>

results in

> This is some inline JSX: [foo value]

instead of

<blockquote>
  This is some inline JSX: [foo value]
</blockquote>

I'm going to go ahead and close this since it will be addressed in MDX v2 (and likely won't update the v1 parser any more) thanks to @wooorm's hard work. If you want to get up and running with it now you can yarn add @mdx-js/mdx@next.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tornordqvist picture tornordqvist  路  19Comments

slorber picture slorber  路  19Comments

AlmeroSteyn picture AlmeroSteyn  路  32Comments

jeetiss picture jeetiss  路  12Comments

aress31 picture aress31  路  19Comments