mdx-js / mdx

Markdown for the component era
https://mdxjs.com
MIT License
17.43k stars 1.14k forks source link

What is the MDXProvider? #197

Closed brandonweiss closed 6 years ago

brandonweiss commented 6 years ago

The section in the README on the MDXProvider is very short and seems to assume a lot… I don't know what this is, why it exists, what it’s for, etc. Could someone expand on it?

silvenon commented 6 years ago

Does this clear things up?

import { MDXProvider } from '@mdx-js/tag'

import One from './one.mdx'
import Two from './two.mdx'
import Three from './three.mdx'

import { Title, Subtitle } from './body'

const components = {
  h1: Title,
  h2: Subtitle,
}

const AppWithoutProvider = () => (
  <div>
    <One components={components} />
    <Two components={components} />
    <Three components={components} />
  </div>
)

const AppWithProvider = () => (
  <MDXProvider components={components}>
    <One />
    <Two />
    <Three />
  </MDXProvider>
)
brandonweiss commented 6 years ago

@silvenon Thanks for helping!

🤔 I think this is heading in the right direction, although there are still some things I don’t understand…

What is the components prop? Is that a React thing? An MDX thing? Does something magical happen with it where it appends components to the end of the component it’s passed into? That doesn’t seem right… but it’s hard to tell because I can't see how components is being consumed inside the child components?

Oh, is the provider a way of passing the same props to every child component? That seems… dangerous? Couldn't you obliterate some props without realizing it? Does it only work with direct children or all children (children of children)?

If that’s what it does… why is it an MDX Provider? It seems like just a generic tool for passing props to all children? There must be something about it I'm missing that ties it into MDX specifically?

silvenon commented 6 years ago

It is a bit magical, yes, but deliberately, for the sake of simplicity of usage.

Let's use this MDX example:

import MyComponent from './my-component'

# Title

<MyComponent />

Lorem ipsum dolor sit amet.

When processed with @mdx-js/loader this basically turns into the following JSX code:

import React from 'react'
import { MDXTag } from '@mdx-js/tag'
import MyComponent from './my-component'

export default ({ components }) => (
  <MDXTag name="wrapper" components={components}>
    <MDXTag name="h1" components={components}>
      Title
    </MDXTag>
    <MyComponent />
    <MDXTag name="p" components={components}>
      Lorem ipsum dolor sit amet.
    </MDXTag>
  </MDXTag>
)

This is what you actually import when you import a MDX file. Notice that the default export takes a components prop. You can either pass this directly or through context using MDXProvider, like in my previous example. The name prop of MDXTag maps to a component defined in the components prop. If components doesn't contain that key, it defaults to that tag name, i. e. "p" becomes <p> like in regular Markdown.

brandonweiss commented 6 years ago

OK, I'm starting to get it, I think. So the components prop is a way of overriding an HTML tag to render a specific component instead. Most probably not to change the tag itself, but to apply some styling to it?

And the MDXProvider is necessary because otherwise there’d be no way to pass the components to the tags inside of an MDX file, because it’s Markdown/MDX (no render function and no props). Correct?

What would happen if you passed something other than a components prop to the MDXProvider? I guess nothing. There’d be no point in proxying other props because you wouldn’t be able to use them inside of MDX.

So the “Component customization“ section and the “MDXProvider” section of the README are… sort of related? I think one helps understand the other.

Would you be open to me improving those sections of the README?

silvenon commented 6 years ago

Most probably not to change the tag itself, but to apply some styling to it?

Yeah. But another example could be to shift all headings. Imagine that you have a MDX file starting with h1, but in a certain context you need headings to start from h2. In that case you could push them all by one. But yes, generally you would use it for styling purposes.

And the MDXProvider is necessary

It's optional. It's only useful if you're frequently using the same components object. That's usually the case for blogs. You can pass components to each imported MDX file, like I showed in the first example. <One components={components}> means that every MDXTag in One will receive the same components object.

What would happen if you passed something other than a components prop to the MDXProvider? I guess nothing.

Yep, probably nothing.

Would you be open to me improving those sections of the README?

I'm not a maintainer, but go right ahead! Everybody loves documentation improvements. 👍

silvenon commented 6 years ago

I'm closing this as a stale issue. This is the current documentation for MDXProvider: https://mdxjs.com/getting-started/#mdxprovider, and we're open to improvements.