---
title: Developing Plugins
---

import { Code } from '@astrojs/starlight/components'
import ConfigVariants from '@components/ConfigVariants.astro'
import PackageManagers from '@components/PackageManagers.astro'

In Expressive Code, all processing of your code blocks and their metadata is performed by plugins and annotations. To render markup around lines or inline ranges of characters, plugins create annotations and attach them to the target lines.

## Adding existing plugins

To add a plugin to Expressive Code that is not installed by default, install its package using your package manager, import its initialization function into your config file, and call it inside the `plugins` array property.

The following example shows how to add the [Collapsible Sections plugin](/plugins/collapsible-sections/) to your Expressive Code configuration. After **installing the plugin** using your package manager, make the following changes to your Expressive Code configuration:

<ConfigVariants
  imports={`
    // Import the plugin's initialization function
    import { pluginCollapsibleSections } from '@expressive-code/plugin-collapsible-sections'
  `}
  settings={`
    plugins: [
      // Call the plugin initialization function inside the \`plugins\` array
      pluginCollapsibleSections(),
    ],
  `}
/>

## Writing your own plugin

To write a new plugin, you need to create a **plugin initialization function** that returns an object matching the interface [`ExpressiveCodePlugin`](/reference/plugin-api/#expressivecodeplugin).

The easiest way to do this is by importing the `definePlugin` helper function from the `@expressive-code/core` package, which will provide your editor with the required type information to help define the plugin correctly. This function takes a single argument, an object containing the properties of your plugin:

```js
// plugins/my-example-plugin.js
import { definePlugin } from '@expressive-code/core'

export function pluginExample() {
  return definePlugin({
    // The only required property is `name`
    name: 'Example that does nothing',
    // Add more properties of `ExpressiveCodePlugin` to make your plugin
    // actually do something (e.g. `baseStyles`, `hooks`, etc.)
    hooks: {
      // Add hooks to perform actions during the plugin's lifecycle
    },
  })
}
```

Then, add the plugin to your Expressive Code configuration just like any other plugin:

<ConfigVariants
  imports={`
    // Import the plugin's initialization function
    // (adding the file extension is recommended for best compatibility)
    import { pluginExample } from './plugins/my-example-plugin.js'
  `}
  settings={`
    plugins: [
      // Call the plugin initialization function inside the \`plugins\` array
      pluginExample(),
    ],
  `}
/>

🎉 Success! From now on, your plugin will be loaded and executed whenever you run your site generator. If you add any hooks, it will be able to process your code blocks.

## Adding options to your plugin

If your plugin needs configuration options, you can add a single `options` argument to your initialization function. This argument must be an object type containing the desired configuration properties.

:::note
Please use sensible default values for all options wherever possible, so that users ideally do not need to pass any options for your plugin to work.
:::

```js ins=/[(](options)[)]/ ins={5}
// plugins/plugin-with-options.js
import { definePlugin } from '@expressive-code/core'

export function pluginWithOptionsExample(options) {
  // Extract the options from the `options` object,
  // and provide sensible default values
  const { option1 = true, option2 = 'hello' } = options || {}
  return definePlugin({
    name: 'Example with options that does nothing',
    hooks: {
      // Add hooks to perform actions during the plugin's lifecycle
      // (you can use the options anywhere in your plugin)
    },
  })
}
```

When adding the plugin to your Expressive Code configuration, pass the desired options as an object to the plugin's initialization function:

<ConfigVariants
  imports={`
    // Import the plugin's initialization function
    // (adding the file extension is recommended for best compatibility)
    import { pluginWithOptionsExample } from './plugins/plugin-with-options.js'
  `}
  settings={`
    plugins: [
      // Call the plugin initialization function inside the \`plugins\` array
      pluginWithOptionsExample({
        // Pass any desired options as object properties
        option1: false,
        option2: 'world',
      }),
    ],
  `}
/>

:::tip
We recommend using [TypeScript](https://www.typescriptlang.org/) to add type annotations to your plugin's options object. This will make it easier for users to discover and understand the available options.
:::

## Adding custom annotations to code

In Expressive Code, annotations are used by plugins to attach semantic information to lines or inline ranges of code. They are used to represent things like syntax highlighting, text markers, comments, errors, warnings, and other semantic information.

Expressive Code provides a built-in [`InlineStyleAnnotation`](/reference/core-api/#inlinestyleannotation) that allows applying simple inline styles to code. See its documentation for a usage example.

To provide full styling flexibility, plugins can also create their own annotations. In the following example, you will create a plugin that uses two custom annotations:
- a `SquigglesAnnotation` to render squiggly red underlines under words surrounded by a pair of `~~` characters, and
- an `ErrorMessageAnnotation` to render error messages in red and italic with a semitransparent background.

For both custom annotations, you will create a class that extends the abstract [`ExpressiveCodeAnnotation`](/reference/core-api/#expressivecodeannotation) class, and provide an implementation for the [`render`](/reference/core-api/#abstract-render) function that transforms its contained AST nodes, e.g. by wrapping them in HTML tags. This function is called by the engine when it's time to render the line the annotation has been attached to.

:::note
If you want to publish your own plugin using the `ExpressiveCodeAnnotation` class, import it from the `@expressive-code/core` package installed as a **peer dependency** of your plugin package. This ensures that your plugin does not cause a version conflict if the user has a different version of Expressive Code installed on their site:

<PackageManagers pkg="@expressive-code/core" peer />
:::

Now, let's create the plugin:

import pluginErrorPreviewCode from '/plugins/plugin-error-preview.js?raw'

<Code code={pluginErrorPreviewCode.replace(/\t/g, '  ')} lang="js" title="plugins/plugin-error-preview.js" />

After saving your new plugin, you need to add it to your Expressive Code configuration:

<ConfigVariants
  imports={`
    // Import the plugin's initialization function
    // (adding the file extension is recommended for best compatibility)
    import { pluginErrorPreview } from './plugins/plugin-error-preview.js'
  `}
  settings={`
    plugins: [
      // Call the plugin initialization function inside the \`plugins\` array
      pluginErrorPreview(),
    ],
  `}
/>

You can now use the new syntax in code blocks to add error annotations to lines! For example, you can use the following markup:

````
```js error-preview
~~consore~~.log('Hello world!')  //! Error: 'consore' is not defined
```
````

This will render the following result:

```js error-preview
~~consore~~.log('Hello world!')  //! Error: 'consore' is not defined
```

🎉 Success! You have created a plugin that uses custom annotations to render squiggly underlines and error messages in your code blocks.

## Adding CSS styles to your plugin

Plugins can add CSS styles by providing a [`baseStyles`](/reference/plugin-api/#basestyles) property in the plugin object returned by the plugin's initialization function.

You can see an example of this in the previous section, where the `baseStyles` property is used to add styles for the squiggly underline and error message annotations.

### Automatic style scoping

All provided styles will be **scoped by default** to Expressive Code, so they will not affect the rest of the page. This means that you can define styles like `font-weight: 800`, or `del { text-decoration: line-through }`, and they will only apply to code blocks. SASS-like nesting is also supported.

If you explicitly want to add **global styles**, you can use the `@at-root` rule or target `:root`, `html` or `body` in your selectors. Please be careful with global styles, as users may not expect your plugin to contain a style like `body { color: red }` that changes the look of the entire page.

### Making your styles theme-dependent

If you want your CSS styles to use colors from the configured themes, you can add a [`styleSettings`](/reference/plugin-api/#stylesettings) property to your plugin object, which will be used to generate CSS variables automatically.

To use the generated CSS variables in your `baseStyles` property, set its value to a function instead of a string. The function will be called by the engine with a `context` argument of the type [`ResolverContext`](/reference/plugin-api/#resolvercontext) that provides access to the names of all generated CSS variables.
