Skip to content

Copy Button

Installation

bun add @rehype-pretty/transformers

Usage

You can use this as a shiki transformer in rehype-pretty-code by passing it to the transformers array.

Options

  • visibility: 'always' | 'hover' (default: 'hover')
  • feedbackDuration: number (default: 3_000)
  • copyIcon: string (default: an inline SVG of a copy icon)
  • successIcon: string (default: an inline SVG of a green checkmark icon)

Examples

with rehype-pretty-code

import { const unified: Processor<undefined, undefined, undefined, undefined, undefined>
Create a new processor.
@example This example shows how a new processor can be created (from `remark`) and linked to **stdin**(4) and **stdout**(4). ```js import process from 'node:process' import concatStream from 'concat-stream' import {remark} from 'remark' process.stdin.pipe( concatStream(function (buf) { process.stdout.write(String(remark().processSync(buf))) }) ) ```@returns New *unfrozen* processor (`processor`). This processor is configured to work the same as its ancestor. When the descendant processor is configured in the future it does not affect the ancestral processor.
unified
} from 'unified'
import const remarkParse: Plugin<[(Readonly<Options> | null | undefined)?], string, Root>
Add support for parsing from markdown.
@this processor.@paramConfiguration (optional).@returns Nothing.
remarkParse
from 'remark-parse'
import function remarkRehype(processor: Processor, options?: Readonly<Options> | null | undefined): TransformBridge (+1 overload)
Turn markdown into HTML. ##### Notes ###### Signature * if a processor is given, runs the (rehype) plugins used on it with a hast tree, then discards the result (*bridge mode*) * otherwise, returns a hast tree, the plugins used after `remarkRehype` are rehype plugins (*mutate mode*) > 👉 **Note**: It’s highly unlikely that you want to pass a `processor`. ###### HTML Raw HTML is available in mdast as `html` nodes and can be embedded in hast as semistandard `raw` nodes. Most plugins ignore `raw` nodes but two notable ones don’t: * `rehype-stringify` also has an option `allowDangerousHtml` which will output the raw HTML. This is typically discouraged as noted by the option name but is useful if you completely trust authors * `rehype-raw` can handle the raw embedded HTML strings by parsing them into standard hast nodes (`element`, `text`, etc). This is a heavy task as it needs a full HTML parser, but it is the only way to support untrusted content ###### Footnotes Many options supported here relate to footnotes. Footnotes are not specified by CommonMark, which we follow by default. They are supported by GitHub, so footnotes can be enabled in markdown with `remark-gfm`. The options `footnoteBackLabel` and `footnoteLabel` define natural language that explains footnotes, which is hidden for sighted users but shown to assistive technology. When your page is not in English, you must define translated values. Back references use ARIA attributes, but the section label itself uses a heading that is hidden with an `sr-only` class. To show it to sighted users, define different attributes in `footnoteLabelProperties`. ###### Clobbering Footnotes introduces a problem, as it links footnote calls to footnote definitions on the page through `id` attributes generated from user content, which results in DOM clobbering. DOM clobbering is this: ```html <p id=x></p> <script>alert(x) // `x` now refers to the DOM `p#x` element</script> ``` Elements by their ID are made available by browsers on the `window` object, which is a security risk. Using a prefix solves this problem. More information on how to handle clobbering and the prefix is explained in *Example: headings (DOM clobbering)* in `rehype-sanitize`. ###### Unknown nodes Unknown nodes are nodes with a type that isn’t in `handlers` or `passThrough`. The default behavior for unknown nodes is: * when the node has a `value` (and doesn’t have `data.hName`, `data.hProperties`, or `data.hChildren`, see later), create a hast `text` node * otherwise, create a `<div>` element (which could be changed with `data.hName`), with its children mapped from mdast to hast as well This behavior can be changed by passing an `unknownHandler`.
@overload@overload@paramdestination Processor or configuration (optional).@paramoptions When a processor was given, configuration (optional).@returnsTransform.
remarkRehype
from 'remark-rehype'
import const rehypeStringify: Plugin<[(Options | null | undefined)?], Root, string>
Plugin to add support for serializing as HTML.
@this processor.@paramConfiguration (optional).@returns Nothing.
rehypeStringify
from 'rehype-stringify'
import { function rehypePrettyCode(options?: Options): void | Transformer<Root, Root>rehypePrettyCode } from 'rehype-pretty-code' import { function transformerCopyButton(options?: CopyButtonOptions): ShikiTransformer
A transformer that adds a copy button to code blocks.
@paramoptions - Options for the copy button behavior and appearance.@paramoptions.feedbackDuration - The duration in milliseconds to show the success icon after copying.@paramoptions.copyIcon - Either data URL svg or inline svg for the copy icon.@paramoptions.successIcon - Either data URL svg or inline svg for the success icon.@returnsA Shiki transformer. find icons at https://icones.js.org - copy the "Data URL" and paste it as the value of `copyIcon` and/or `successIcon`.@example```ts import { codeToHtml } from 'shiki' import { transformerCopyButton } from '@rehype-pretty/copy-button' const html = await codeToHtml(`console.log('hello, world')`, { lang: 'ts', theme: 'houston', transformers: [ transformerCopyButton({ visibility: 'always', feedbackDuration: 2_000, }), ], }) ```
transformerCopyButton
} from '@rehype-pretty/transformers'
const const file: VFilefile = await function unified(): Processor<undefined, undefined, undefined, undefined, undefined>
Create a new processor.
@example This example shows how a new processor can be created (from `remark`) and linked to **stdin**(4) and **stdout**(4). ```js import process from 'node:process' import concatStream from 'concat-stream' import {remark} from 'remark' process.stdin.pipe( concatStream(function (buf) { process.stdout.write(String(remark().processSync(buf))) }) ) ```@returns New *unfrozen* processor (`processor`). This processor is configured to work the same as its ancestor. When the descendant processor is configured in the future it does not affect the ancestral processor.
unified
()
.Processor<undefined, undefined, undefined, undefined, undefined>.use<[], string, Root>(plugin: Plugin<[], string, Root>, ...parameters: [] | [boolean]): Processor<Root, undefined, undefined, undefined, undefined> (+2 overloads)
Configure the processor to use a plugin, a list of usable values, or a preset. If the processor is already using a plugin, the previous plugin configuration is changed based on the options that are passed in. In other words, the plugin is not added a second time. > **Note**: `use` cannot be called on *frozen* processors. > Call the processor first to create a new unfrozen processor.
@example There are many ways to pass plugins to `.use()`. This example gives an overview: ```js import {unified} from 'unified' unified() // Plugin with options: .use(pluginA, {x: true, y: true}) // Passing the same plugin again merges configuration (to `{x: true, y: false, z: true}`): .use(pluginA, {y: false, z: true}) // Plugins: .use([pluginB, pluginC]) // Two plugins, the second with options: .use([pluginD, [pluginE, {}]]) // Preset with plugins and settings: .use({plugins: [pluginF, [pluginG, {}]], settings: {position: false}}) // Settings only: .use({settings: {position: false}}) ```@template{Array<unknown>} [Parameters=[]]@template{Node | string | undefined} [Input=undefined]@template[Output=Input]@overload@overload@overload@paramvalue Usable value.@paramparameters Parameters, when a plugin is given as a usable value.@returnsCurrent processor.
use
(const remarkParse: Plugin<[(Readonly<Options> | null | undefined)?], string, Root>
Add support for parsing from markdown.
@this processor.@paramConfiguration (optional).@returns Nothing.
remarkParse
)
.Processor<Root, undefined, undefined, undefined, undefined>.use<[], Root, Root>(plugin: Plugin<[], Root, Root>, ...parameters: [] | [boolean]): Processor<Root, Root, Root, undefined, undefined> (+2 overloads)
Configure the processor to use a plugin, a list of usable values, or a preset. If the processor is already using a plugin, the previous plugin configuration is changed based on the options that are passed in. In other words, the plugin is not added a second time. > **Note**: `use` cannot be called on *frozen* processors. > Call the processor first to create a new unfrozen processor.
@example There are many ways to pass plugins to `.use()`. This example gives an overview: ```js import {unified} from 'unified' unified() // Plugin with options: .use(pluginA, {x: true, y: true}) // Passing the same plugin again merges configuration (to `{x: true, y: false, z: true}`): .use(pluginA, {y: false, z: true}) // Plugins: .use([pluginB, pluginC]) // Two plugins, the second with options: .use([pluginD, [pluginE, {}]]) // Preset with plugins and settings: .use({plugins: [pluginF, [pluginG, {}]], settings: {position: false}}) // Settings only: .use({settings: {position: false}}) ```@template{Array<unknown>} [Parameters=[]]@template{Node | string | undefined} [Input=undefined]@template[Output=Input]@overload@overload@overload@paramvalue Usable value.@paramparameters Parameters, when a plugin is given as a usable value.@returnsCurrent processor.
use
(function remarkRehype(processor: Processor, options?: Readonly<Options> | null | undefined): TransformBridge (+1 overload)
Turn markdown into HTML. ##### Notes ###### Signature * if a processor is given, runs the (rehype) plugins used on it with a hast tree, then discards the result (*bridge mode*) * otherwise, returns a hast tree, the plugins used after `remarkRehype` are rehype plugins (*mutate mode*) > 👉 **Note**: It’s highly unlikely that you want to pass a `processor`. ###### HTML Raw HTML is available in mdast as `html` nodes and can be embedded in hast as semistandard `raw` nodes. Most plugins ignore `raw` nodes but two notable ones don’t: * `rehype-stringify` also has an option `allowDangerousHtml` which will output the raw HTML. This is typically discouraged as noted by the option name but is useful if you completely trust authors * `rehype-raw` can handle the raw embedded HTML strings by parsing them into standard hast nodes (`element`, `text`, etc). This is a heavy task as it needs a full HTML parser, but it is the only way to support untrusted content ###### Footnotes Many options supported here relate to footnotes. Footnotes are not specified by CommonMark, which we follow by default. They are supported by GitHub, so footnotes can be enabled in markdown with `remark-gfm`. The options `footnoteBackLabel` and `footnoteLabel` define natural language that explains footnotes, which is hidden for sighted users but shown to assistive technology. When your page is not in English, you must define translated values. Back references use ARIA attributes, but the section label itself uses a heading that is hidden with an `sr-only` class. To show it to sighted users, define different attributes in `footnoteLabelProperties`. ###### Clobbering Footnotes introduces a problem, as it links footnote calls to footnote definitions on the page through `id` attributes generated from user content, which results in DOM clobbering. DOM clobbering is this: ```html <p id=x></p> <script>alert(x) // `x` now refers to the DOM `p#x` element</script> ``` Elements by their ID are made available by browsers on the `window` object, which is a security risk. Using a prefix solves this problem. More information on how to handle clobbering and the prefix is explained in *Example: headings (DOM clobbering)* in `rehype-sanitize`. ###### Unknown nodes Unknown nodes are nodes with a type that isn’t in `handlers` or `passThrough`. The default behavior for unknown nodes is: * when the node has a `value` (and doesn’t have `data.hName`, `data.hProperties`, or `data.hChildren`, see later), create a hast `text` node * otherwise, create a `<div>` element (which could be changed with `data.hName`), with its children mapped from mdast to hast as well This behavior can be changed by passing an `unknownHandler`.
@overload@overload@paramdestination Processor or configuration (optional).@paramoptions When a processor was given, configuration (optional).@returnsTransform.
remarkRehype
)
.Processor<Root, Root, Root, undefined, undefined>.use<[options?: Options | undefined], Root, Root>(plugin: Plugin<[options?: Options | undefined], Root, Root>, ...parameters: [boolean] | [options?: Options | undefined]): Processor<...> (+2 overloads)
Configure the processor to use a plugin, a list of usable values, or a preset. If the processor is already using a plugin, the previous plugin configuration is changed based on the options that are passed in. In other words, the plugin is not added a second time. > **Note**: `use` cannot be called on *frozen* processors. > Call the processor first to create a new unfrozen processor.
@example There are many ways to pass plugins to `.use()`. This example gives an overview: ```js import {unified} from 'unified' unified() // Plugin with options: .use(pluginA, {x: true, y: true}) // Passing the same plugin again merges configuration (to `{x: true, y: false, z: true}`): .use(pluginA, {y: false, z: true}) // Plugins: .use([pluginB, pluginC]) // Two plugins, the second with options: .use([pluginD, [pluginE, {}]]) // Preset with plugins and settings: .use({plugins: [pluginF, [pluginG, {}]], settings: {position: false}}) // Settings only: .use({settings: {position: false}}) ```@template{Array<unknown>} [Parameters=[]]@template{Node | string | undefined} [Input=undefined]@template[Output=Input]@overload@overload@overload@paramvalue Usable value.@paramparameters Parameters, when a plugin is given as a usable value.@returnsCurrent processor.
use
(function rehypePrettyCode(options?: Options): void | Transformer<Root, Root>rehypePrettyCode, {
Options.transformers?: ShikiTransformer[] | undefinedtransformers: [ function transformerCopyButton(options?: CopyButtonOptions): ShikiTransformer
A transformer that adds a copy button to code blocks.
@paramoptions - Options for the copy button behavior and appearance.@paramoptions.feedbackDuration - The duration in milliseconds to show the success icon after copying.@paramoptions.copyIcon - Either data URL svg or inline svg for the copy icon.@paramoptions.successIcon - Either data URL svg or inline svg for the success icon.@returnsA Shiki transformer. find icons at https://icones.js.org - copy the "Data URL" and paste it as the value of `copyIcon` and/or `successIcon`.@example```ts import { codeToHtml } from 'shiki' import { transformerCopyButton } from '@rehype-pretty/copy-button' const html = await codeToHtml(`console.log('hello, world')`, { lang: 'ts', theme: 'houston', transformers: [ transformerCopyButton({ visibility: 'always', feedbackDuration: 2_000, }), ], }) ```
transformerCopyButton
({
CopyButtonOptions.visibility?: "hover" | "always" | undefinedvisibility: 'always', CopyButtonOptions.feedbackDuration?: number | undefinedfeedbackDuration: 3_000, }), ], }) .Processor<Root, Root, Root, undefined, undefined>.use<[], Root, string>(plugin: Plugin<[], Root, string>, ...parameters: [] | [boolean]): Processor<Root, Root, Root, Root, string> (+2 overloads)
Configure the processor to use a plugin, a list of usable values, or a preset. If the processor is already using a plugin, the previous plugin configuration is changed based on the options that are passed in. In other words, the plugin is not added a second time. > **Note**: `use` cannot be called on *frozen* processors. > Call the processor first to create a new unfrozen processor.
@example There are many ways to pass plugins to `.use()`. This example gives an overview: ```js import {unified} from 'unified' unified() // Plugin with options: .use(pluginA, {x: true, y: true}) // Passing the same plugin again merges configuration (to `{x: true, y: false, z: true}`): .use(pluginA, {y: false, z: true}) // Plugins: .use([pluginB, pluginC]) // Two plugins, the second with options: .use([pluginD, [pluginE, {}]]) // Preset with plugins and settings: .use({plugins: [pluginF, [pluginG, {}]], settings: {position: false}}) // Settings only: .use({settings: {position: false}}) ```@template{Array<unknown>} [Parameters=[]]@template{Node | string | undefined} [Input=undefined]@template[Output=Input]@overload@overload@overload@paramvalue Usable value.@paramparameters Parameters, when a plugin is given as a usable value.@returnsCurrent processor.
use
(const rehypeStringify: Plugin<[(Options | null | undefined)?], Root, string>
Plugin to add support for serializing as HTML.
@this processor.@paramConfiguration (optional).@returns Nothing.
rehypeStringify
)
.Processor<Root, Root, Root, Root, string>.process(file?: Compatible | undefined): Promise<VFile> (+1 overload)
Process the given file as configured on the processor. > **Note**: `process` freezes the processor if not already *frozen*. > **Note**: `process` performs the parse, run, and stringify phases.
@overload@overload@paramfile File (optional); typically `string` or `VFile`]; any value accepted as `x` in `new VFile(x)`.@paramdone Callback (optional).@returns Nothing if `done` is given. Otherwise a promise, rejected with a fatal error or resolved with the processed file. The parsed, transformed, and compiled value is available at `file.value` (see note). > **Note**: unified typically compiles by serializing: most > compilers return `string` (or `Uint8Array`). > Some compilers, such as the one configured with > [`rehype-react`][rehype-react], return other values (in this case, a > React tree). > If you’re using a compiler that doesn’t serialize, expect different > result values. > > To register custom results in TypeScript, add them to > {@linkcode CompileResultMap}. [rehype-react]: https://github.com/rehypejs/rehype-react
process
(`\`\`\`js\nconsole.log('Hello, World!')\n\`\`\``)
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without calling `require('console')`. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v22.x/lib/console.js)
console
.Console.log(...data: any[]): void (+2 overloads)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)
log
(
var String: StringConstructor
(value?: any) => string
Allows manipulation and formatting of text strings and determination and location of substrings within strings.
String
(const file: VFilefile))

with shiki

import { codeToHtml } from 'shiki'
 
const code = await codeToHtml('console.log("Hello World")', {
  lang: 'ts',
  theme: 'vitesse-light',
  transformers: [
    transformerCopyButton({
      visibility: 'always',
      feedbackDuration: 3_000,
    }),
  ]
})