Third Party Platform
Jump To Online Code Editing Platform
vitepress-demo-plugin supports jumping to popular online code editing platforms, such as Stackblitz, Codesandbox, etc., with two ways of local opening and global opening.
Local
Add the stackblitz/codesandbox attribute in the <demo /> component to take effect on a single <demo /> component. For example:
<demo vue="../demos/demo.vue" stackblitz="true" codesandbox="true" />The rendering effect is as follows:
Global
Add the following configuration in .vitepress/config.ts to take effect on all <demo /> components.
import { defineConfig } from 'vitepress';
import { vitepressDemoPlugin } from 'vitepress-demo-plugin/markdown';
export default defineConfig({
// other configs...
markdown: {
config(md) {
md.use(vitepressDemoPlugin, {
stackblitz: {
show: true,
},
codesandbox: {
show: true,
},
});
},
},
});Preset Files And Codes
vitepress-demo-plugin presets some file configurations, so that you can open stackblitz/codesandbox and other platforms to preview your <demo /> without a separate configuration file in most cases. The preset files and codes for different platforms and demo types are as follows:
Expand to view the stackblitz platform Vue preset files and codes
<!-- Will be dynamically replaced with your demo code -->import { createApp } from "vue";
import App from "./App.vue";
const app = createApp(App);
app.mount("#app");{
"installDependencies": false,
"startCommand": "npm install && npm run dev"
}<!DOCTYPE html>
<html>
<head></head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>{
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
},
"dependencies": {
"vue": "latest",
// Will be automatically added based on the import dependencies in your demo
},
"devDependencies": {
"vite": "latest",
"typescript": "latest",
"@vitejs/plugin-vue": "latest",
"@vitejs/plugin-vue-jsx": "latest"
}
}{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowImportingTsExtensions": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
},
"include": [
"src"
]
}import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
export default defineConfig({
plugins: [vue(), vueJsx()],
});Expand to view the stackblitz platform React preset files and code
<!-- Will be dynamically replaced with your demo code -->import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
const root = createRoot(document.querySelector("#app"));
root.render(<App />);{
"installDependencies": false,
"startCommand": "npm install && npm run dev"
}<!DOCTYPE html>
<html>
<head></head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>{
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
},
"dependencies": {
"react": "latest",
"@emotion/styled": "latest",
"react-dom": "latest",
"@emotion/react": "latest"
},
"devDependencies": {
"typescript": "latest",
"vite": "latest",
"@vitejs/plugin-react": "latest",
"@types/react": "latest",
"@types/react-dom": "latest"
}
}{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowImportingTsExtensions": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react"
},
"include": [
"src"
]
}import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
});Expand to view the stackblitz platform Html preset files and codes
<!-- Expand to view the stackblitz platform Html preset files and codes -->Expand to view codesandbox platform Vue preset files and codes
<!-- Expand to view codesandbox platform Vue preset files and codes -->import { createApp } from "vue";
import App from "./App.vue";
const app = createApp(App);
app.mount("#app");<!DOCTYPE html>
<html>
<head></head>
<body>
<div id="app"></div>
</body>
</html>{
"version": "0.0.0",
"private": true,
"dependencies": {
"vue": "latest"
},
"devDependencies": {
"typescript": "latest",
"@vue/cli-plugin-babel": "latest"
}
}{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowImportingTsExtensions": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
},
"include": [
"src"
]
}Expand to view the codesandbox platform React preset files and codes
<!-- Expand to view the stackblitz platform Html preset files and codes -->import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
const root = createRoot(document.querySelector("#app"));
root.render(<App />);<!DOCTYPE html>
<html>
<head></head>
<body>
<div id="app"></div>
</body>
</html>{
"version": "0.0.0",
"private": true,
"dependencies": {
"react": "latest",
"@emotion/styled": "latest",
"react-dom": "latest",
"@emotion/react": "latest"
},
"devDependencies": {
"typescript": "latest",
"@types/react": "latest",
"@types/react-dom": "latest"
}
}{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowImportingTsExtensions": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react"
},
"include": [
"src"
]
}Expand to view codesandbox platform Html preset files and codes
<!-- Expand to view the stackblitz platform Html preset files and codes -->Custom Files And Code
vitepress-demo-plugin supports custom files and code. You can add templates configuration in .vitepress/config.ts to replace the code of the preset file or add a new file. The types of templates are as follows:
type Template = {
scope: 'global' | 'vue' | 'react' | 'html' | string;
files: {
[filename: string]: string; // Code
};
}
type Templates = Template[];All Kinds
When scope is set to global, it means that the template is effective for all types of demo components. Take the stackblitz platform as an example:
import { defineConfig } from 'vitepress';
import { vitepressDemoPlugin } from 'vitepress-demo-plugin/markdown';
export default defineConfig({
// other configs...
markdown: {
config(md) {
md.use(vitepressDemoPlugin, {
stackblitz: {
show: true,
templates: [
{
scope: 'global', // This is effective for all types of demo components.
files: {
// Add new file
'print.js': `console.log("Hello!Vitepress Demo Plugin")`,
// Replace the default index.html file
'index.html': `<!DOCTYPE html><html><body><div id="app"></div></body><script src="print.js"></script></html>`,
}
},
],
}
});
},
}
});Single Kind
When scope is set to vue/react/html, it means that the template is only valid for demo components of the corresponding type. Take the following demo as an example, it is only valid for demo components of the Vue type:
import { defineConfig } from 'vitepress';
import { vitepressDemoPlugin } from 'vitepress-demo-plugin/markdown';
export default defineConfig({
// other configs...
markdown: {
config(md) {
md.use(vitepressDemoPlugin, {
stackblitz: {
show: true,
templates: [
{
scope: 'global', // This is effective for all types of demo components.
files: {
// Add new file
'print.js': `console.log("Hello!Vitepress Demo Plugin")`,
// Replace the default index.html file
'index.html': `<!DOCTYPE html><html><body><div id="app"></div></body><script src="print.js"></script></html>`,
},
},
{
scope: 'vue', // Only valid for Vue demo components
files: {
// Replace the default main.ts file
'main.ts': `import { createApp } from "vue";\nimport App from "./App.vue";\nconst app = createApp(App);\napp.mount("#app");`,
}
},
]
}
});
},
}
});Customize The Demo Scope
You can also customize the name of scope to indicate that the template is only valid for demo components of the corresponding type. For example:
import { defineConfig } from 'vitepress';
import { vitepressDemoPlugin } from 'vitepress-demo-plugin/markdown';
export default defineConfig({
// other configs...
markdown: {
config(md) {
md.use(vitepressDemoPlugin, {
stackblitz: {
show: true,
templates: [
{
scope: 'global', // This is effective for all types of demo components.
files: {
// Add new file
'print.js': `console.log("Hello!Vitepress Demo Plugin")`,
// Replace the default index.html file
'index.html': `<!DOCTYPE html><html><body><div id="app"></div></body><script src="print.js"></script></html>`,
},
},
{
scope: 'vue', // Only valid for Vue demo components
files: {
// Replace the default main.ts file
'main.ts': `import { createApp } from "vue";\nimport App from "./App.vue";\nconst app = createApp(App);\napp.mount("#app");`,
}
},
{
scope: 'myScope', // Only valid for scope demo components
files: {
// Replace the default main.ts file
'main.ts': `console.log("this is a custom template")`,
}
},
]
}
});
},
}
});Now that you have defined a template named myScope, you can use the scope property to make the template available to a specific demo component.
<demo vue="../demos/demo.vue" scope="myScope" />Custom Playground
In addition to codesandbox and stackblitz, you can configure links to other playground platforms. The playground configuration is defined as follows:
export type PlaygroundConfig = {
// The URL to open
url: string | ((content: string) => string);
// Custom file processing logic
fn?: (files: Record<string, string>) => string;
// Some playgrounds require a specific entry file name. For example,
// the Element Plus playground requires App.vue. VitePress replaces the
// corresponding demo entry file name with the value configured here.
entryName?: {
vue?: string; // Defaults to App.vue
react?: string; // Defaults to App.tsx
html?: string; // Defaults to index.html
};
};
export type Playground = {
// Whether to show the playground button for all demos
show: boolean;
// Platform templates
templates?: PlatformTemplate[];
// Playground configuration. A name is required when using the array form.
config: PlaygroundConfig | (PlaygroundConfig & { name: string })[];
};Basic Configuration
vitepress-demo-plugin provides built-in logic for processing playground code parameters. It works with many playground platforms, such as the Vue SFC Playground and Element Plus Playground. You only need to configure the url:
import { defineConfig } from 'vitepress';
import { vitepressDemoPlugin } from 'vitepress-demo-plugin/markdown';
export default defineConfig({
// other configs...
markdown: {
config(md) {
md.use(vitepressDemoPlugin, {
playground: {
config: {
url: 'https://element-plus.run',
},
},
});
},
}
});Set playground to true to use the built-in playground processing logic.
<demo vue="../demos/ele.vue" playground="true" />Configure Multiple Playgrounds
If you want different demos to open in different playgrounds, you can provide multiple config entries. The following example adds CodePlayer as a second playground.
First, change
playground.configto an array and set aname:tsimport { defineConfig } from 'vitepress'; import { vitepressDemoPlugin } from 'vitepress-demo-plugin/markdown'; export default defineConfig({ // other configs... markdown: { config(md) { md.use(vitepressDemoPlugin, { playground: { config: { url: 'https://element-plus.run', }, config: [ { name: 'elementPlus', url: 'https://element-plus.run', }, ], }, }); }, } });Add a config named
codeplayerand customize itsurl:tsimport { defineConfig } from 'vitepress'; import { vitepressDemoPlugin } from 'vitepress-demo-plugin/markdown'; export default defineConfig({ // other configs... markdown: { config(md) { md.use(vitepressDemoPlugin, { playground: { config: [ { name: 'elementPlus', url: 'https://element-plus.run', }, { name: 'codeplayer', url: (content: string) => `https://play.fe-dev.cn/?entry=index.html#${content}`, }, ], }, }); }, } });Add the
codeplayerscope using the format required by CodePlayer:tsimport { defineConfig } from 'vitepress'; import { vitepressDemoPlugin } from 'vitepress-demo-plugin/markdown'; const indexHtml = ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>CodePlayer</title> </head> <body> <div id="app"></div> </body> <script type="module"> import './main.ts'; </script> </html> `.trim(); const mainTs = ` import { createApp } from 'vue'; import App from './App.vue'; const app = createApp(App); app.mount('#app'); `.trim(); const importJson = ` { "imports": { "vue": "https://esm.sh/vue@latest" } }`.trim(); export default defineConfig({ // other configs... markdown: { config(md) { md.use(vitepressDemoPlugin, { playground: { config: [ { name: 'elementPlus', url: 'https://element-plus.run', }, { name: 'codeplayer', url: (content: string) => `https://play.fe-dev.cn/?entry=index.html#${content}`, }, ], templates: [ { scope: 'codeplayer', files: { 'main.ts': mainTs, 'index.html': indexHtml, 'import-map.json': importJson, } }, ] }, }); }, } });Customize the
fnfunction according to CodePlayer's serialization format:tsimport { defineConfig } from 'vitepress'; import { vitepressDemoPlugin } from 'vitepress-demo-plugin/markdown'; const indexHtml = ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>CodePlayer</title> </head> <body> <div id="app"></div> </body> <script type="module"> import './main.ts'; </script> </html> `.trim(); const mainTs = ` import { createApp } from 'vue'; import App from './App.vue'; const app = createApp(App); app.mount('#app'); `.trim(); const importJson = ` { "imports": { "vue": "https://esm.sh/vue@latest" } }`.trim(); export default defineConfig({ // other configs... markdown: { config(md) { md.use(vitepressDemoPlugin, { playground: { config: [ { name: 'elementPlus', url: 'https://element-plus.run', }, { name: 'codeplayer', url: (content: string) => `https://play.fe-dev.cn/?entry=index.html&activeFile=App.vue#${content}`, fn: (files: Record<string, string>) => { return btoa(JSON.stringify(files)); }, }, ], templates: [ { scope: 'codeplayer', files: { 'main.ts': mainTs, 'index.html': indexHtml, 'import-map.json': importJson, }, }, ], }, }); }, } });
You can now open each demo in a different playground:
- Open in Element Plus Playground
<demo vue="../demos/ele.vue" playground="elementPlus" />- Open in CodePlayer
<demo
vue="../demos/multiple.vue"
:vueFiles="['../demos/multiple.vue', '../demos/constant/students.ts']" playground="codeplayer"
scope="codeplayer"
/>