Import statements in theme-live-codeblock not working. I read the react-live docs and it does look like they do support importing modules - does docusaurus expose the needed scope?
Yes
theme-live-codeblock according to these instructions@material-ui/corefile.mdx attempt to import components from @material-ui/core.mdx file as well as the ``` jsx live blockRender the imported component
.mdx (keep in mind I can use the imported component within the .mdx file just fine, just can't use it within the ```jsx live block..):
```jsx live block:
// from package.json
"dependencies": {
"@docusaurus/core": "^2.0.0-alpha.55",
"@docusaurus/preset-classic": "^2.0.0-alpha.55",
"@docusaurus/theme-live-codeblock": "^2.0.0-alpha.39",
"@material-ui/core": "^4.10.0",
"@material-ui/icons": "^4.9.1",
"classnames": "^2.2.6",
},
After browsing through the repo, it doesn't look like docusaurus lets you configure react-live scope..
As a workaround, this is what I did:
Slightly modified
Playground
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import {
LiveProvider,
LiveEditor,
LiveError,
LivePreview
} from 'react-live'
import classnames from 'classnames';
import usePrismTheme from './usePrismTheme.js';
import * as styles from './styles.module.css';
function Playground({children, /* theme, */ transformCode, ...props}) {
const theme = usePrismTheme();
return (
<LiveProvider
code={children.replace(/\n$/, '')}
transformCode={transformCode || ((code) => `${code};`)}
theme={theme}
{...props}>
<div
className={classnames(
styles.playgroundHeader,
styles.playgroundEditorHeader,
)}>
Live Editor
</div>
<LiveEditor />
<div
className={classnames(
styles.playgroundHeader,
styles.playgroundPreviewHeader,
)}>
Result
</div>
<div className={styles.playgroundPreview}>
<LivePreview />
<LiveError />
</div>
</LiveProvider>
);
}
export default Playground;
NOT modified
usePrismTheme
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import defaultTheme from 'prism-react-renderer/themes/palenight';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import useThemeContext from '@theme/hooks/useThemeContext';
const usePrismTheme = () => {
const {
siteConfig: {
themeConfig: {prism = {}},
},
} = useDocusaurusContext();
const {isDarkTheme} = useThemeContext();
const lightModeTheme = prism.theme || defaultTheme;
const darkModeTheme = prism.darkTheme || lightModeTheme;
const prismTheme = isDarkTheme ? darkModeTheme : lightModeTheme;
return prismTheme;
};
export default usePrismTheme;
NOT modified
styles.module.css
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
.playgroundHeader {
letter-spacing: 0.08rem;
padding: 0.75rem;
text-transform: uppercase;
font-weight: bold;
}
.playgroundEditorHeader {
background: var(--ifm-color-emphasis-600);
color: var(--ifm-color-content-inverse);
}
.playgroundPreviewHeader {
background: var(--ifm-color-emphasis-200);
color: var(--ifm-color-content);
}
.playgroundPreview {
border: 1px solid var(--ifm-color-emphasis-200);
border-bottom-left-radius: var(--ifm-global-radius);
border-bottom-right-radius: var(--ifm-global-radius);
position: relative;
padding: 1rem;
}
From there, I import the Playground into an .mdx file and it worked! I did try to use react-live components on their own, which worked, but styling was not right. That is why I copied those components - to keep styling.
MyFile.mdx:---
id: some_id
title: My Live Code Demo
---
import LiveCodeBlock from '../Playground';
import { Button } from '@material-ui/core';
### Custom components
<LiveCodeBlock scope={{ Button }}>
{`
function MyButton() {
return <Button color="secondary" onClick={() => alert("Hello, world!")}>Click me</Button>
}
`}
</LiveCodeBlock>
Which works perfectly!

Ultimately, with my solution above, there were still some minor differences in styling.. So I started digging into the codebase and it looks like my solution very similar (in theory, at least lol) to swizzling.

Despite the warning, swizzling MDXComponents was the easiest way to accomplish this:
yarn swizzle @docusaurus/theme-classic MDXComponents
This creates a file at src/theme/MDXComponents/index.js - (click on me to see edited file)
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import Link from '@docusaurus/Link';
import CodeBlock from '@theme/CodeBlock';
import Heading from '@theme/Heading';
import styles from './styles.module.css';
//-------------------------------------------------------/
////// Import the component you need in scope
import Button from "@material-ui/core/Button";
//-------------------------------------------------------/
////// Object with components I need in scope
const SCOPE = {
Button,
}
export default {
code: (props) => {
const {children} = props;
if (typeof children === 'string') {
//-------------------------------------------------------/
return <CodeBlock {...props} scope={SCOPE} />;
/**
* REPLACED THE LINE BELOW WITH THE LINE ABOVE
*/
//// return <CodeBlock {...props} />;
//-------------------------------------------------------/
}
return children;
},
a: (props) => {
if (/\.[^./]+$/.test(props.href)) {
// eslint-disable-next-line jsx-a11y/anchor-has-content
return <a {...props} />;
}
return <Link {...props} />;
},
pre: (props) => <div className={styles.mdxCodeBlock} {...props} />,
h1: Heading('h1'),
h2: Heading('h2'),
h3: Heading('h3'),
h4: Heading('h4'),
h5: Heading('h5'),
h6: Heading('h6'),
};
Then you can just use it in your markdown:
``` jsx live
function ButtonTest() {
return (
<Button onClick={() => alert('omg it worked')} color="secondary">Click Me</Button>
)
}
```
This worked perfectly for me - no need for a custom component now.
Hi,
I'm going to reopen because this is something I noticed inspecting the code 3 days ago, and wanted to fix.
We should be able to import components in MDX, and they should become automatically available in the playgrounds of the mdx page. Afaik my discussions with Chris Biscardy there's a useComponents() in MDX that can be called to get the comps to pass to react-live scope.
I'm not too familiar with the libraries/technology used in this repo (lerna, mdx, react-live..) Hell, I just started with docusaurus like 3 days ago...but if there's anything you need help with, feel free to reach out.
Hey @slorber - just wanted to let you know that it doesn't look like that useMDXComponents hook returns imported components. It looks like it returns the data already in /MDXComponents/index.js - it doesn't return imported components. I'm not sure if there's a way to have MDX tell us what has been imported or not (so that we can add it to scope).. FYI.
Unfortunately you are right @oze4
I found back my discussion with Chris Biscardi here: https://twitter.com/sebastienlorber/status/1252899627126358016
There is a useMDXScope hook, but it's not from MDX, it's from MDX, it's from the Gatsby MDX plugin.
I've looked at its code and it's probably not so easy to port it fast to Docusaurus, as it uses a babel plugin to extract imports from the MDX generated JSX, write them to disk in a dedicated file...
Will probably come back to this later when we have shipped more urgent features.
In the meantime, I think we still can make something about it, like allowing to more easily provide components to the react-live scope with swizzle. The scope may not be per-MDX file, but having a global scope is probably good enough for most usecases.
Also, afaik we don't have yet a "wrapRootElement" API but I've seen this is something we want. This would enable to use the MDXProvider
@oze4 I've made a PR to support more officially a workflow to provide some components to the react-live scope. It's not as good as the gatsby integration as I hoped (as you have to configure available scope globally) but I think it's good enough.
Can you tell me if this solves your usecase?
I think it would permit you to swizzle a "smaller" part of the live editor plugin
Hey @slorber - I did notice that Gatsby uses a custom hook, useMDXScope from the gatsby-plugin-mdx which I did test out as well. Unless I'm using it incorrectly, the useMDXScope hook also did not update with imported components - it returned the same data as useMDXComponents. (After all it looks like useMDXScope is a wrapper for useMDXComponents?)
When I imported a component in an .mdx file, the useMDXScope hook did not pick up on that. Again, I could be using it incorrectly, but that is also something I looked into. While it looks like they have the 'right idea', I'm not sure it's working as intended.
Thanks for everything!
Edit: FYI - this was one of the articles I found, which I used as part of my testing.
Yes it's normal as it's proprietary to gatsby, it assumes the gatsby-node.js code has run and written something to the FS, otherwise it just returns no additional context :)
Awesome. Thanks for your input. What's weird is that I was using it in Gatsby and it was still returning no additional context. I wanted to see how they handled this so I built out a little demo Gatsby site..
With that being said, it's probably something I was doing wrong.
Cheers!
Didn't test with Gatsby either but I guess it should work ^^ Does the doc of the PR I wrote seems good for your usecase?
Yes sir. Thank you!
Implemented in https://github.com/facebook/docusaurus/pull/2826.