Markdown to LiveCodes
Markdown and MDX code blocks can be easily converted to interactive LiveCodes playgrounds.
The playgrounds can run any of the supported languages in LiveCodes, and can be customized to any of the configuration options.
A fenced code block in Markdown can be rendered as a LiveCodes playground by adding the livecodes
parameter to the code block language meta.
This is provided as plugins for markdown-it, marked and remark. These plugins allow the seamless integration with most of the popular frameworks like Astro, Docusaurus, Next.js, Storybook, VitePress, etc. See the section "Using with Frameworks" for getting started.
Demo
This is an example code block:
```jsx
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times.</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
export default App;
```
The above code block is normally rendered like this:
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times.</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
export default App;
The code block can instead be rendered as an interactive playground by adding the livecodes
parameter to the code block language meta:
```jsx livecodes
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times.</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
export default App;
```
to be displayed like this:
The playground can be customized by setting options that are applied to all code blocks or by meta parameters that are applied to individual code blocks.
Alternatively, the code block can be kept as it is, and a button or a link (Edit in LiveCodes) is appended, below the code block, that opens the code in a LiveCodes playground.
This is achieved by adding the render=button
or render=link
parameter to the code block language meta.
This displays a button:
```jsx livecodes render=button
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times.</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
export default App;
```
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times.</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
export default App;
While this displays a link:
```jsx livecodes render=link
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times.</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
export default App;
```
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times.</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
export default App;
Packages
All the functionality described here can be achieved using any of the following packages:
markdown-it-livecodes
: A markdown-it plugin.marked-livecodes
: A marked plugin.remark-livecodes
: A remark plugin.gatsby-remark-livecodes
: A gatsby plugin.
See the section "Using with Frameworks" for using the plugins with popular frameworks like Astro, Docusaurus, Next.js, Storybook, VitePress, etc.
Usage
markdown-it-livecodes
To use the markdown-it-livecodes
plugin, first install it:
npm install markdown-it markdown-it-livecodes
Then it can be used like this:
import markdownIt from "markdown-it";
import markdownItLivecodes from "markdown-it-livecodes";
const input = "```js livecodes \nconsole.log('Hello World!');\n```";
const output = markdownIt()
.use(markdownItLivecodes, {
/* options */
})
.render(input);
console.log(output); // <iframe ...></iframe>
marked-livecodes
To use the marked-livecodes
plugin, first install it:
npm install marked marked-livecodes
Then it can be used like this:
import marked from "marked";
import markedLivecodes from "marked-livecodes";
const input = "```js livecodes \nconsole.log('Hello World!');\n```";
const output = await marked
.use(markedLivecodes, {
/* options */
})
.parse(input);
console.log(output); // <iframe ...></iframe>
remark-livecodes
To use the remark-livecodes
plugin, first install it:
npm install remark remark-livecodes
Then it can be used like this:
import { remark } from "remark";
import remarkLivecodes from "remark-livecodes";
const input = "```js livecodes \nconsole.log('Hello World!');\n```";
const output = await remark()
.use(remarkLivecodes, {
/* options */
})
.process(input);
console.log(String(output)); // <iframe ...></iframe>
gatsby-remark-livecodes
See usage with Gatsby.
Options
Options can be passed to the plugins. These options apply to all code blocks.
These options include LiveCodes SDK embed options (except headless
).
Example:
const output = await remark()
.use(remarkLivecodes, {
loading: "click",
params: {
console: "open"
theme: "light",
}
})
.process(input);
In addition, the following options are also available:
render
: The render mode for the LiveCodes playgrounds. This can be one of the following:playground
(default): Replaces the code block with an iframe that displays the LiveCodes playground. By default,"simple"
mode is used, but this can be changed in options or meta parameters.link
: Keeps the code block as it is, and appends a link (Edit in LiveCodes), below the code block, that opens the code in a LiveCodes playground.button
: Keeps the code block as it is, and appends a button (Edit in LiveCodes), below the code block, that opens the code in a LiveCodes playground.
meta
: Keeps the code block as it is, and adds the URL of the playground to thedata-livecodes-url
attribute of the<code>
element. In addition, inremark-livecodes
the URL is added to the AST (node.data.livecodesUrl
andnode.data.hProperties.dataLivecodesUrl
). Inmarkdown-it-livecodes
the URL is added toenv.livecodesUrl
. This can be used by other plugins (e.g. to display a custom run button overlying the code block).
height
: The height of the playground iframe.className
: The class name to be applied to the iframe, link or button. Note: If the class name of the button contains"dark"
(e.g."dark-btn"
), the dark button will be used.
auto
: When set totrue
, it automatically enables thelivecodes
parameter for all code blocks without having to explicitly add it. This is useful when you have a large number of code blocks and don't want to add thelivecodes
parameter to each code block. To disable this for a specific code block, add thelivecodes=false
meta parameter to the code block.
Meta Parameters
Individual code blocks can be configured using meta parameters. These are key/value pairs, separated by spaces, that are added after the language name.
Meta parameters of code blocks override the options passed to the plugin.
Example:
```jsx livecodes render=button className=dark-btn console=open
import { useState, useEffect } from "react";
export default () => {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("count:", count);
}, [count]);
return (
<div>
<p>You clicked {count} times.</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
};
```
All LiveCodes configuration query parameters can be used as code block meta parameters (e.g. ```js livecodes console=open theme=light
). See the LiveCodes configuration docs for more information.
In addition, the following meta parameters are available:
livecodes
: Enables the LiveCodes playground for the code block. This can be omitted if theauto
option is set totrue
. Whenlivecodes
is set tofalse
, the code block is not handled by the plugin.render
: The render mode. See the Options section for more information.height
: The height of the playground iframe.className
: The class name for the playground iframe, link or button.lang
: This overrides the language of the code block (e.g.```jsx livecodes lang=react
or```py livecodes lang=py-wasm
). See the Languages docs for more language information.
Using with Frameworks
This guide shows how to use the suitable plugin in different frameworks.
Astro
(demo - code on GitHub)
This is an example for adding the remark-livecodes
plugin to astro.config.mjs
file:
import { defineConfig } from "astro/config";
import mdx from "@astrojs/mdx";
import remarkLivecodes from "remark-livecodes";
export default defineConfig({
// ...
integrations: [mdx()],
markdown: {
remarkPlugins: [
[
remarkLivecodes,
{
/* options */
},
],
],
},
});
Docusaurus
(demo - code on GitHub)
This is an example for adding the remark-livecodes
plugin to docusaurus.config.js
file:
export default {
presets: [
[
'classic',
{
docs: {
// ...
remarkPlugins: [
[require('remark-livecodes'), { /* options */ }],
],
},
},
],
],
// ...
};
Gatsby
(demo - code on GitHub)
This is an example for adding the gatsby-remark-livecodes
plugin to gatsby-config.js
file:
module.exports = {
// ...
plugins: [
// ...
{
resolve: 'gatsby-transformer-remark',
options: {
plugins: [
{
resolve: 'gatsby-remark-livecodes',
options: { /* options */ },
},
],
},
},
],
};
Next.js
(demo - code on GitHub)
See Next.js docs for using markdown and MDX in Next.js.
This is an example for adding the remark-livecodes
plugin to next.config.js
file:
import createMDX from "@next/mdx";
const nextConfig = {
// ...
pageExtensions: ["js", "jsx", "md", "mdx", "ts", "tsx"],
};
const withMDX = createMDX({
options: {
remarkPlugins: [
['remark-livecodes', { /* other options */ }],
],
},
});
export default withMDX(nextConfig);
When using Turbopack for local development, check the guide for using plugins with Turbopack.
react-markdown
react-markdown
is a React component to render markdown.
This is an example for using the remark-livecodes
plugin with react-markdown
:
import Markdown from 'react-markdown';
import remarkLivecodes from 'remark-livecodes';
const markdown =
'```jsx livecodes\nexport default () => <h1>Hello World</h1>\n```';
export default () => (
<Markdown remarkPlugins={[remarkLivecodes]}>{markdown}</Markdown>
);
Storybook
(demo - code on GitHub)
This is an example for adding the remark-livecodes
plugin to storybook/main.js
file:
import remarkLivecodes from "remark-livecodes";
export default {
// ...
addons: [
// ...
{
name: "@storybook/addon-docs",
options: {
mdxPluginOptions: {
mdxCompileOptions: {
remarkPlugins: [
[remarkLivecodes, { /* options */ }],
],
},
},
},
},
],
};
VitePress
(demo - code on GitHub)
This is an example for adding the markdown-it-livecodes
plugin to vitepress.config.js
file:
import { defineConfig } from "vitepress";
import markDownItLivecodes from "markdown-it-livecodes";
export default defineConfig({
// ...
markdown: {
config: (md) => {
md.use(markDownItLivecodes);
},
},
});