Codeblocks Plugin

Welcome to the Codeblocks Plugin documentation! This guide will provide you with an overview of the Codeblocks plugin and its features.

What is the Codeblocks Plugin?

The Codeblocks Plugin is a powerful tool that enhances the standard code block functionality in Markdown. It allows you to create visually appealing and customizable code blocks that can be used to highlight code snippets in your documentation.

Key Features

  • Syntax Highlighting: Automatically highlight code syntax for various programming languages.
  • TwoSlash Hovers: Opt in to TypeScript-powered hover and query output for richer examples.
  • Line Numbers: Display line numbers alongside your code blocks for better readability.
  • Line Highlighting: Highlight specific lines of code for emphasis.
  • Add/Remove Lines: Highlight add or remove lines of code with ease.
  • Tabs: Organize your code blocks into tabs for easy navigation.
  • Copy to Clipboard: Add a button to copy code snippets to the clipboard with a single click.
  • Customizable Styles: Apply custom styles to your code blocks to match your documentation’s theme.
  • Responsive Design: Ensure your code blocks look great on all devices.
  • Fixed Height: (optional) Maintain a consistent height for your code blocks.

Supported Language Syntaxes

NameAliasesComment
markupDefault, when none applied. Allows codeblocks in codeblocks
bash
javascriptjs
typescriptts
sass
scss
css
json
xml
html
diffSpecial grammar

Examples

Here are some examples of what you can achieve with the Codeblocks Plugin:

Standard Code Block

console.log('Hello, world!')
```js
console.log('Hello, world!')
```

Code Block with Title

My Title

console.log('Hello, world!')
```js My Title
console.log('Hello, world!')
```

Code Block with TwoSlash

Add the twoslash attribute to TypeScript or JavaScript examples when you want inferred type information, compiler diagnostics, or query output. Hover an identifier in the example below to see the type tooltip.

const const count: 1count = 1
const const label: stringlabel = const count: 1count.Number.toFixed(fractionDigits?: number): string
Returns a string representing a number in fixed-point notation.
@paramfractionDigits Number of digits after the decimal point. Must be in the range 0 - 20, inclusive.
toFixed
(0)
```ts [twoslash]
const count = 1
const label = count.toFixed(0)
```

If you add a // ^? query marker, TwoSlash renders that query result persistently below the matching expression. That is useful for teaching types directly in the page, while normal identifier details remain hover-based.

const selectedIcon = 'event' as type const = "event"const
const selectedIcon: "event"
```ts [twoslash]
const selectedIcon = 'event' as const
//    ^?
```

Code Block with Restricted Height

export function containersPlugin(
  md: MarkdownIt,
  containers: ContainerDetails[],
  createContainer: CreateContainerFn,
): void {
  if (!Array.isArray(containers) || containers.length === 0) {
    console.warn('No containers provided to containersPlugin.')
    return
  }

  if (typeof createContainer == 'function') {
    throw new Error('Invalid createContainer function provided to containersPlugin.')
  }

  containers.forEach(({ type, defaultTitle }) => {
    try {
      md.use(...createContainer(container, type, defaultTitle, md))
    } catch (error) {
      console.error(`Failed to create container for type: ${type}`, error)
    }
  })
}
```js [maxheight=300px]
export function containersPlugin(
  md: MarkdownIt,
  containers: ContainerDetails[],
  createContainer: CreateContainerFn,
): void {
  if (!Array.isArray(containers) || containers.length === 0) {
    console.warn('No containers provided to containersPlugin.')
    return
  }

  if (typeof createContainer == 'function') {
    throw new Error('Invalid createContainer function provided to containersPlugin.')
  }

  containers.forEach(({ type, defaultTitle }) => {
    try {
      md.use(...createContainer(container, type, defaultTitle, md))
    } catch (error) {
      console.error(`Failed to create container for type: ${type}`, error)
    }
  })
}
```

Code Block with Line Numbers

 1export function containersPlugin(
 2  md: MarkdownIt,
 3  containers: ContainerDetails[],
 4  createContainer: CreateContainerFn,
 5): void {
 6  if (!Array.isArray(containers) || containers.length === 0) {
 7    console.warn('No containers provided to containersPlugin.')
 8    return
 9  }
10
11  if (typeof createContainer == 'function') {
12    throw new Error('Invalid createContainer function provided to containersPlugin.')
13  }
14
15  containers.forEach(({ type, defaultTitle }) => {
16    try {
17      md.use(...createContainer(container, type, defaultTitle, md))
18    } catch (error) {
19      console.error(`Failed to create container for type: ${type}`, error)
20    }
21  })
22}
```js [numbered]
export function containersPlugin(
  md: MarkdownIt,
  containers: ContainerDetails[],
  createContainer: CreateContainerFn,
): void {
  if (!Array.isArray(containers) || containers.length === 0) {
    console.warn('No containers provided to containersPlugin.')
    return
  }

  if (typeof createContainer !== 'function') {
    throw new Error('Invalid createContainer function provided to containersPlugin.')
  }

  containers.forEach(({ type, defaultTitle }) => {
    try {
      md.use(...createContainer(container, type, defaultTitle, md))
    } catch (error) {
      console.error(`Failed to create container for type: ${type}`, error)
    }
  })
}
```

Code Block with Highlighted Lines

export function containersPlugin(
  md: MarkdownIt,
  containers: ContainerDetails[],
  createContainer: CreateContainerFn,
): void {
  if (!Array.isArray(containers) || containers.length === 0) {
    console.warn('No containers provided to containersPlugin.')
    return
  }

  if (typeof createContainer !== 'function') {
    throw new Error('Invalid createContainer function provided to containersPlugin.')
  }

  containers.forEach(({ type, defaultTitle }) => {
    try {
      md.use(...createContainer(container, type, defaultTitle, md))
    } catch (error) {
      console.error(`Failed to create container for type: ${type}`, error)
    }
  })
}
```js [highlight=6-9,15]
export function containersPlugin(
  md: MarkdownIt,
  containers: ContainerDetails[],
  createContainer: CreateContainerFn,
): void {
  if (!Array.isArray(containers) || containers.length === 0) {
    console.warn('No containers provided to containersPlugin.')
    return
  }

  if (typeof createContainer !== 'function') {
    throw new Error('Invalid createContainer function provided to containersPlugin.')
  }

  containers.forEach(({ type, defaultTitle }) => {
    try {
      md.use(...createContainer(container, type, defaultTitle, md))
    } catch (error) {
      console.error(`Failed to create container for type: ${type}`, error)
    }
  })
}
```

Code Block with Internal Highlights

export function containersPlugin(
  md: MarkdownIt,
  containers: ContainerDetails[],
  createContainer: CreateContainerFn,
): void {
  if (!Array.isArray(containers) || containers.length === 0) {
    console.warn('No containers provided to containersPlugin.')
    return
  }

  if (typeof createContainer !== 'function') {
    throw new Error('Invalid createContainer function provided to containersPlugin.')
  }

  containers.forEach(({ type, defaultTitle }) => {
    try {
      md.use(...createContainer(container, type, defaultTitle, md))
    } catch (error) {
      console.error(`Failed to create container for type: ${type}`, error)
    }
  })
}

Look for the [[! highlight]] on individual lines.

```js
export function containersPlugin(
  md: MarkdownIt,
  containers: ContainerDetails[],
  createContainer: CreateContainerFn,
): void {
  if (!Array.isArray(containers) || containers.length === 0) { [[! highlight]]
    console.warn('No containers provided to containersPlugin.') [[! highlight]]
    return [[! highlight]]
  } [[! highlight]]

  if (typeof createContainer !== 'function') {
    throw new Error('Invalid createContainer function provided to containersPlugin.')
  }

  containers.forEach(({ type, defaultTitle }) => { [[! highlight]]
    try {
      md.use(...createContainer(container, type, defaultTitle, md))
    } catch (error) {
      console.error(`Failed to create container for type: ${type}`, error)
    }
  })
}
```

Code Block with Add/Remove (add/rem) Lines

 export function containersPlugin(
   md: MarkdownIt,
   containers: ContainerDetails[],
   createContainer: CreateContainerFn,
 ): void {
+  if (!Array.isArray(containers) || containers.length === 0) {
+    console.warn('No containers provided to containersPlugin.')
+    return
+  }
 
   if (typeof createContainer !== 'function') {
     throw new Error('Invalid createContainer function provided to containersPlugin.')
   }
 
   containers.forEach(({ type, defaultTitle }) => {
     try {
       md.use(...createContainer(container, type, defaultTitle, md))
     } catch (error) {
-      console.error(`Failed to create container for type: ${type}`, error)
     }
   })
 }
```js [add=6-9 rem=19]
export function containersPlugin(
  md: MarkdownIt,
  containers: ContainerDetails[],
  createContainer: CreateContainerFn,
): void {
  if (!Array.isArray(containers) || containers.length === 0) {
    console.warn('No containers provided to containersPlugin.')
    return
  }

  if (typeof createContainer !== 'function') {
    throw new Error('Invalid createContainer function provided to containersPlugin.')
  }

  containers.forEach(({ type, defaultTitle }) => {
    try {
      md.use(...createContainer(container, type, defaultTitle, md))
    } catch (error) {
      console.error(`Failed to create container for type: ${type}`, error)
    }
  })
}
```

Code Block with Internal Add/Remove (add/rem) Lines

 export function containersPlugin(
   md: MarkdownIt,
   containers: ContainerDetails[],
   createContainer: CreateContainerFn,
 ): void {
+  if (!Array.isArray(containers) || containers.length === 0) {
+    console.warn('No containers provided to containersPlugin.')
+    return
+  }
 
   if (typeof createContainer !== 'function') {
     throw new Error('Invalid createContainer function provided to containersPlugin.')
   }
 
   containers.forEach(({ type, defaultTitle }) => {
     try {
       md.use(...createContainer(container, type, defaultTitle, md))
     } catch (error) {
-      console.error(`Failed to create container for type: ${type}`, error)
     }
   })
 }

Look for the [[! add]] and [[! rem]] on individual lines.

```js
export function containersPlugin(
  md: MarkdownIt,
  containers: ContainerDetails[],
  createContainer: CreateContainerFn,
): void {
  if (!Array.isArray(containers) || containers.length === 0) { [[! add]]
    console.warn('No containers provided to containersPlugin.') [[! add]]
    return [[! add]]
  } [[! add]]

  if (typeof createContainer !== 'function') {
    throw new Error('Invalid createContainer function provided to containersPlugin.')
  }

  containers.forEach(({ type, defaultTitle }) => {
    try {
      md.use(...createContainer(container, type, defaultTitle, md))
    } catch (error) {
      console.error(`Failed to create container for type: ${type}`, error) [[! rem]]
    }
  })
}
```

Code Block Diff

Use diff when you need literal + / - markers to remain in copyable output. The add / rem options are great for visual emphasis, but they do not copy as leading + / - characters.

function createContainer(
    container: Container,
    containerType: string,
    defaultTitle: string
+    md: MarkdownIt
  ): [Container, string, ContainerOptions] {
    const containerTypeLen = containerType.length;

    return [
      container,
      containerType,
      {
        render(tokens: Token[], idx: number): string {
          const token = tokens[idx];
-          const title = token.info.trim().slice(containerTypeLen).trim() || defaultTitle;
+          // Get the title from token info or use defaultTitle
+          const rawTitle = token.info.trim().slice(containerTypeLen).trim() || defaultTitle

+        // Process the title as inline markdown
+        const titleHtml = md ? md.renderInline(rawTitle) : rawTitle

          if (containerType === 'details') {
            return token.nesting === 1
-              ? `<details class="markdown-note markdown-note--${containerType}"><summary class="markdown-note__title">${title}/<summary>\n`
+              ? `<details class="markdown-note markdown-note--${containerType}"><summary class="markdown-note__title">${titleHtml}</summary>\n`
              : '</details>\n';
          }

          return token.nesting === 1
-            ? `<div class="markdown-note markdown-note--${containerType}"><p class="markdown-note__title">${title}</p>\n`
+            ? `<div class="markdown-note markdown-note--${containerType}"><p class="markdown-note__title">${titleHtml}</p>\n`
            : '</div>\n';
        },
      },
    ];
  }

Look for the + and - on individual lines at the far-left.

```diff
  function createContainer(
    container: Container,
    containerType: string,
    defaultTitle: string
+    md: MarkdownIt
  ): [Container, string, ContainerOptions] {
    const containerTypeLen = containerType.length;

    return [
      container,
      containerType,
      {
        render(tokens: Token[], idx: number): string {
          const token = tokens[idx];
-          const title = token.info.trim().slice(containerTypeLen).trim() || defaultTitle;
+          // Get the title from token info or use defaultTitle
+          const rawTitle = token.info.trim().slice(containerTypeLen).trim() || defaultTitle

+        // Process the title as inline markdown
+        const titleHtml = md ? md.renderInline(rawTitle) : rawTitle

          if (containerType === 'details') {
            return token.nesting === 1
-              ? `<details class="markdown-note markdown-note--${containerType}"><summary class="markdown-note__title">${title}/<summary>\n`
+              ? `<details class="markdown-note markdown-note--${containerType}"><summary class="markdown-note__title">${titleHtml}</summary>\n`
              : '</details>\n';
          }

          return token.nesting === 1
-            ? `<div class="markdown-note markdown-note--${containerType}"><p class="markdown-note__title">${title}</p>\n`
+            ? `<div class="markdown-note markdown-note--${containerType}"><p class="markdown-note__title">${titleHtml}</p>\n`
            : '</div>\n';
        },
      },
    ];
  }
```

Tabs


pnpm add @md-plugins/md-plugin-codeblocks
```tabs
<<| bash pnpm |>>
pnpm add @md-plugins/md-plugin-codeblocks
<<| bash bun |>>
bun add @md-plugins/md-plugin-codeblocks
<<| bash yarn |>>
yarn add @md-plugins/md-plugin-codeblocks
<<| bash npm |>>
npm install @md-plugins/md-plugin-codeblocks
```

Advanced Tabs


1  console.log('Hello, world!'); // Line 1
2  const a = 10; // Line 2
3 +const b = 20; // Line 3
4  console.log(a + b); // Line 4
5 -console.log('This line will be removed'); // Line 5
```tabs
<<| javascript [numbered highlight=2,4 add=3 rem=5] javascript |>>
console.log('Hello, world!'); // Line 1
const a = 10; // Line 2
const b = 20; // Line 3
console.log(a + b); // Line 4
console.log('This line will be removed'); // Line 5
<<| typescript [numbered highlight=2,4 add=3 rem=5] typescript |>>
console.log('Hello, TypeScript!'); // Line 1
const a: number = 10; // Line 2
const b: number = 20; // Line 3
console.log(a + b); // Line 4
console.log('This line will be removed'); // Line 5
<<| bash [numbered highlight=2,4 add=3 rem=5] bash |>>
echo 'Hello, Bash!' # Line 1
VAR1=10 # Line 2
VAR2=20 # Line 3
echo $((VAR1 + VAR2)) # Line 4
echo 'This line will be removed' # Line 5
```

Advanced Tabs with Title

Examples

1  console.log('Hello, world!'); // Line 1
2  const a = 10; // Line 2
3 +const b = 20; // Line 3
4  console.log(a + b); // Line 4
5 -console.log('This line will be removed'); // Line 5
```tabs Examples
<<| javascript [numbered highlight=2,4 add=3 rem=5] javascript |>>
console.log('Hello, world!'); // Line 1
const a = 10; // Line 2
const b = 20; // Line 3
console.log(a + b); // Line 4
console.log('This line will be removed'); // Line 5
<<| typescript [numbered highlight=2,4 add=3 rem=5] typescript |>>
console.log('Hello, TypeScript!'); // Line 1
const a: number = 10; // Line 2
const b: number = 20; // Line 3
console.log(a + b); // Line 4
console.log('This line will be removed'); // Line 5
<<| bash [numbered highlight=2,4 add=3 rem=5] bash |>>
echo 'Hello, Bash!' # Line 1
VAR1=10 # Line 2
VAR2=20 # Line 3
echo $((VAR1 + VAR2)) # Line 4
echo 'This line will be removed' # Line 5
```

Name

The official NPM name is @md-plugins/md-plugin-codeblocks.

Installation

You can install the Codeblocks plugin using npm, yarn, pnpm, or bun. Choose your preferred method below:


pnpm add @md-plugins/md-plugin-codeblocks

Configuration

After installing the plugin, you need to configure it in your Markdown-It setup. Here’s an example of how to do that:

import MarkdownIt from 'markdown-it'
import { codeblocksPlugin } from '@md-plugins/md-plugin-codeblocks'

const md = new MarkdownIt()

md.use(codeblocksPlugin, {
  defaultLang: 'javascript', // Optional: Set the default language
  containerComponent: 'MarkdownPrerender', // Optional: Customize the container component
  copyButtonComponent: 'MarkdownCopyButton', // Optional: Customize the copy button component
  tabPanelTagName: 'q-tab-panel', // Optional: Customize the tab panel tag name
  tabPanelTagClass: 'q-pa-none', // Optional: Customize the tab panel tag class
  preClass: 'markdown-code', // Optional: Customize the class for the pre tag
  codeClass: '', // Optional: Customize the class for the code tag
  pageScripts: [
    "import MarkdownPrerender from '@/.q-press/components/MarkdownPrerender'", // ts file
    "import MarkdownCopyButton from '@/.q-press/components/MarkdownCopyButton.vue'",
  ], // Optional: Include page scripts
  langList: [
    { name: 'javascript', aliases: 'javascript|js' },
    { name: 'typescript', aliases: 'typescript|ts' },
  ], // Optional: Customize Shiki languages
})

// Now you can use the Codeblocks plugin in your Markdown content
const result = md.render("```javascript\nconsole.log('Hello, world!');\n```")
console.log(result)

Options

The Codeblocks plugin accepts the following options:

  • defaultLang: The default language to use if none is detected. Default is markup.
  • containerComponent: The component used to wrap code blocks. Default is MarkdownPrerender.
  • copyButtonComponent: The component used to render the copy button. Default is MarkdownCopyButton.
  • tabPanelTagName: The component name for the tab panel. Default is q-tab-panel.
  • tabPanelTagClass: The class(es) to be used with the tab panel. Default is q-pa-none.
  • preClass: The class to be used for the pre tag. Default is markdown-pre.
  • codeClass: The class to be used for the code tag. Default is markdown-code.
  • pageScripts: An array of page scripts to be included.
  • langList: Optional Shiki languages configuration array. Each item can have a name, optional aliases, and customCopy boolean.

When defaultLang is markup, the plugin will use the markup language for code blocks which means no highlighting will be applied.

TIP

You can use markup to display other code blocks as they will not be processed.

Advanced Configuration

For more advanced configurations, you can combine the Codeblocks plugin with other Markdown-It plugins to enhance your Markdown content further. Here’s an example:

import MarkdownIt from 'markdown-it'
import { codeblocksPlugin } from '@md-plugins/md-plugin-codeblocks'
import markdownItAnchor from 'markdown-it-anchor'
import markdownItToc from 'markdown-it-toc-done-right'

const md = new MarkdownIt()

md.use(codeblocksPlugin, {
  defaultLang: 'javascript', // Set the default language
  containerComponent: 'MarkdownPrerender', // Customize the container component
  copyButtonComponent: 'MarkdownCopyButton', // Customize the copy button component
  tabPanelTagName: 'q-tab-panel', // Customize the tab panel tag name
  tabPanelTagClass: 'q-pa-none', // Customize the tab panel tag class
  preClass: 'markdown-pre', // Customize the class for the pre tag
  codeClass: 'markdown-code', // Customize the class for the code tag
  pageScripts: [
    "import MarkdownPrerender from '@/.q-press/components/MarkdownPrerender'", // ts file
    "import MarkdownCopyButton from '@/.q-press/components/MarkdownCopyButton.vue'",
  ], // Include page scripts
  langList: [
    { name: 'javascript', aliases: 'javascript|js' },
    { name: 'typescript', aliases: 'typescript|ts' },
  ], // Customize Shiki languages
})
  .use(markdownItAnchor)
  .use(markdownItToc)

// Now you can use the Codeblocks plugin along with other plugins in your Markdown content
const result = md.render(
  "# Table of Contents\n\n[[toc]]\n\n```javascript\nconsole.log('Hello, world!');\n```",
)
console.log(result)

Support

If you have any questions or need assistance, please refer to the FAQ or reach out to our support team.

Happy coding!