Gathering detailed insights and metrics for unified
Gathering detailed insights and metrics for unified
Gathering detailed insights and metrics for unified
Gathering detailed insights and metrics for unified
lodash-unified
A union entrypoint of lodash for both ESModule and Commonjs.
@formatjs/intl-unified-numberformat
Ponyfill for intl unified numberformat proposal
unified-message-control
Enable, disable, and ignore messages from unified processors
unified-engine
unified engine to process multiple files, lettings users configure from the file system
Parse, inspect, transform, and serialize content with syntax trees
npm install unified
Typescript
Module System
Node Version
NPM Version
JavaScript (87.2%)
TypeScript (12.8%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
4,776 Stars
423 Commits
115 Forks
34 Watchers
1 Branches
29 Contributors
Updated on Jul 09, 2025
Latest Version
11.0.5
Package Id
unified@11.0.5
Unpacked Size
142.51 kB
Size
30.18 kB
File Count
11
NPM Version
10.8.1
Node Version
22.0.0
Published on
Jun 19, 2024
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
unified lets you inspect and transform content with plugins.
processor()
processor.compiler
processor.data([key[, value]])
processor.freeze()
processor.parse(file)
processor.parser
processor.process(file[, done])
processor.processSync(file)
processor.run(tree[, file][, done])
processor.runSync(tree[, file])
processor.stringify(tree[, file])
processor.use(plugin[, options])
CompileResultMap
CompileResults
Compiler
Data
Parser
Pluggable
PluggableList
Plugin
PluginTuple
Preset
ProcessCallback
Processor
RunCallback
Settings
TransformCallback
Transformer
unified is two things:
unified
(this project) is the core package, used in 1.3m+ projects on GH,
to process content with pluginsSeveral ecosystems are built on unified around different kinds of content. Notably, remark (markdown), rehype (HTML), and retext (natural language). These ecosystems can be connected together.
unifiedjs.com
In some cases, you are already using unified.
For example, it’s used in MDX, Gatsby, Docusaurus, etc.
In those cases, you don’t need to add unified
yourself but you can include
plugins into those projects.
But the real fun (for some) is to get your hands dirty and work with syntax trees and build with it yourself. You can create those projects, or things like Prettier, or your own site generator. You can connect utilities together and make your own plugins that check for problems and transform from one thing to another.
When you are dealing with one type of content (such as markdown), you can use
the main package of that ecosystem instead (so remark
).
When you are dealing with different kinds of content (such as markdown and
HTML), it’s recommended to use unified
itself, and pick and choose the plugins
you need.
This package is ESM only. In Node.js (version 16+), install with npm:
1npm install unified
In Deno with esm.sh
:
1import {unified} from 'https://esm.sh/unified@11'
In browsers with esm.sh
:
1<script type="module"> 2 import {unified} from 'https://esm.sh/unified@11?bundle' 3</script>
1import rehypeDocument from 'rehype-document' 2import rehypeFormat from 'rehype-format' 3import rehypeStringify from 'rehype-stringify' 4import remarkParse from 'remark-parse' 5import remarkRehype from 'remark-rehype' 6import {unified} from 'unified' 7import {reporter} from 'vfile-reporter' 8 9const file = await unified() 10 .use(remarkParse) 11 .use(remarkRehype) 12 .use(rehypeDocument, {title: '👋🌍'}) 13 .use(rehypeFormat) 14 .use(rehypeStringify) 15 .process('# Hello world!') 16 17console.error(reporter(file)) 18console.log(String(file))
Yields:
1no issues found
1<!doctype html> 2<html lang="en"> 3 <head> 4 <meta charset="utf-8"> 5 <title>👋🌍</title> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 </head> 8 <body> 9 <h1>Hello world!</h1> 10 </body> 11</html>
unified
is an interface for processing content with syntax trees.
Syntax trees are a representation of content understandable to programs.
Those programs, called plugins, take these trees and inspect and
modify them.
To get to the syntax tree from text, there is a parser.
To get from that back to text, there is a compiler.
This is the process of a processor.
1| ........................ process ........................... | 2| .......... parse ... | ... run ... | ... stringify ..........| 3 4 +--------+ +----------+ 5Input ->- | Parser | ->- Syntax Tree ->- | Compiler | ->- Output 6 +--------+ | +----------+ 7 X 8 | 9 +--------------+ 10 | Transformers | 11 +--------------+
Processors process content.
On its own, unified
(the root processor) doesn’t work.
It needs to be configured with plugins to work.
For example:
1const processor = unified() 2 .use(remarkParse) 3 .use(remarkRehype) 4 .use(rehypeDocument, {title: '👋🌍'}) 5 .use(rehypeFormat) 6 .use(rehypeStringify)
That processor can do different things. It can:
parse
)run
)stringify
)process
)Every processor implements another processor. To create a processor, call another processor. The new processor is configured to work the same as its ancestor. But when the descendant processor is configured in the future it does not affect the ancestral processor.
When processors are exposed from a module (for example, unified
itself) they
should not be configured directly, as that would change their behavior for all
module users.
Those processors are frozen and they should be called to create
a new processor before they are used.
When processing a document, metadata is gathered about that document.
vfile
is the file format that stores data, metadata, and messages
about files for unified and plugins.
There are several utilities for working with these files.
The syntax trees used in unified are unist nodes.
A tree represents a whole document and each node is a plain JavaScript
object with a type
field.
The semantics of nodes and the format of syntax trees is defined by other
projects:
There are many utilities for working with trees listed in each aforementioned
project and maintained in the syntax-tree
organization.
These utilities are a level lower than unified itself and are building blocks
that can be used to make plugins.
Around each syntax tree is an ecosystem that focusses on that particular kind of content. At their core, they parse text to a tree and compile that tree back to text. They also provide plugins that work with the syntax tree, without requiring that the end user has knowledge about that tree.
Each aforementioned ecosystem comes with a large set of plugins that you can pick and choose from to do all kinds of things.
remarkjs/awesome-remark
·
remark-plugin
topicrehypejs/awesome-rehype
·
rehype-plugin
topicretextjs/awesome-retext
·
retext-plugin
topicThere are also a few plugins that work in any ecosystem:
unified-diff
— ignore unrelated messages in GitHub Actions and Travisunified-infer-git-meta
— infer metadata of a document from Gitunified-message-control
— enable, disable, and ignore messages from contentProcessors are configured with plugins or with the
data
method.
Most plugins also accept configuration through options.
See each plugin’s readme for more info.
unified can integrate with the file system through
unified-engine
.
CLI apps can be created with unified-args
, Gulp plugins with
unified-engine-gulp
, and language servers with
unified-language-server
.
A streaming interface can be created with unified-stream
.
The API provided by unified
allows multiple files to be processed and
gives access to metadata (such as lint messages):
1import rehypeStringify from 'rehype-stringify' 2import remarkParse from 'remark-parse' 3import remarkPresetLintMarkdownStyleGuide from 'remark-preset-lint-markdown-style-guide' 4import remarkRehype from 'remark-rehype' 5import remarkRetext from 'remark-retext' 6import retextEnglish from 'retext-english' 7import retextEquality from 'retext-equality' 8import {unified} from 'unified' 9import {reporter} from 'vfile-reporter' 10 11const file = await unified() 12 .use(remarkParse) 13 .use(remarkPresetLintMarkdownStyleGuide) 14 .use(remarkRetext, unified().use(retextEnglish).use(retextEquality)) 15 .use(remarkRehype) 16 .use(rehypeStringify) 17 .process('*Emphasis* and _stress_, you guys!') 18 19console.error(reporter(file)) 20console.log(String(file))
Yields:
11:16-1:24 warning Emphasis should use `*` as a marker emphasis-marker remark-lint 21:30-1:34 warning `guys` may be insensitive, use `people`, `persons`, `folks` instead gals-man retext-equality 3 4⚠ 2 warnings
1<p><em>Emphasis</em> and <em>stress</em>, you guys!</p>
Ecosystems can be combined in two modes.
Bridge mode transforms the tree from one format (origin) to another (destination). A different processor runs on the destination tree. Afterwards, the original processor continues with the origin tree.
Mutate mode also transforms the syntax tree from one format to another. But the original processor continues transforming the destination tree.
In the previous example (“Programming interface”), remark-retext
is used in
bridge mode: the origin syntax tree is kept after retext is done; whereas
remark-rehype
is used in mutate mode: it sets a new syntax tree and discards
the origin tree.
The following plugins lets you combine ecosystems:
remark-retext
— turn markdown into natural languageremark-rehype
— turn markdown into HTMLrehype-retext
— turn HTML into natural languagerehype-remark
— turn HTML into markdownThis package exports the identifier unified
(the root processor
).
There is no default export.
processor()
Create a new processor.
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.
This example shows how a new processor can be created (from remark
) and linked
to stdin(4) and stdout(4).
1import process from 'node:process' 2import concatStream from 'concat-stream' 3import {remark} from 'remark' 4 5process.stdin.pipe( 6 concatStream(function (buf) { 7 process.stdout.write(String(remark().processSync(buf))) 8 }) 9)
processor.compiler
Compiler to use (Compiler
, optional).
processor.data([key[, value]])
Configure the processor with info available to all plugins. Information is stored in an object.
Typically, options can be given to a specific plugin, but sometimes it makes sense to have information shared with several plugins. For example, a list of HTML elements that are self-closing, which is needed during all phases.
👉 Note: setting information cannot occur on frozen processors. Call the processor first to create a new unfrozen processor.
👉 Note: to register custom data in TypeScript, augment the
Data
interface.
processor = processor.data(key, value)
processor = processor.data(dataset)
value = processor.data(key)
dataset = processor.data()
key
(keyof Data
, optional) — field to getvalue
(Data[key]
) — value to setvalues
(Data
) — values to setThe current processor when setting (processor
), the value at
key
when getting (Data[key]
), or the entire dataset when
getting without key (Data
).
This example show how to get and set info:
1import {unified} from 'unified' 2 3const processor = unified().data('alpha', 'bravo') 4 5processor.data('alpha') // => 'bravo' 6 7processor.data() // => {alpha: 'bravo'} 8 9processor.data({charlie: 'delta'}) 10 11processor.data() // => {charlie: 'delta'}
processor.freeze()
Freeze a processor.
Frozen processors are meant to be extended and not to be configured directly.
When a processor is frozen it cannot be unfrozen. New processors working the same way can be created by calling the processor.
It’s possible to freeze processors explicitly by calling .freeze()
.
Processors freeze automatically when .parse()
, .run()
, .runSync()
,
.stringify()
, .process()
, or .processSync()
are called.
The current processor (processor
).
This example, index.js
, shows how rehype
prevents extensions to itself:
1import rehypeParse from 'rehype-parse' 2import rehypeStringify from 'rehype-stringify' 3import {unified} from 'unified' 4 5export const rehype = unified().use(rehypeParse).use(rehypeStringify).freeze()
That processor can be used and configured like so:
1import {rehype} from 'rehype' 2import rehypeFormat from 'rehype-format' 3// … 4 5rehype() 6 .use(rehypeFormat) 7 // …
A similar looking example is broken as operates on the frozen interface. If this behavior was allowed it would result in unexpected behavior so an error is thrown. This is not valid:
1import {rehype} from 'rehype' 2import rehypeFormat from 'rehype-format' 3// … 4 5rehype 6 .use(rehypeFormat) 7 // …
Yields:
1~/node_modules/unified/index.js:426
2 throw new Error(
3 ^
4
5Error: Cannot call `use` on a frozen processor.
6Create a new processor first, by calling it: use `processor()` instead of `processor`.
7 at assertUnfrozen (~/node_modules/unified/index.js:426:11)
8 at Function.use (~/node_modules/unified/index.js:165:5)
9 …
processor.parse(file)
Parse text to a syntax tree.
👉 Note:
parse
freezes the processor if not already frozen.
👉 Note:
parse
performs the parse phase, not the run phase or other phases.
file
(Compatible
) — file to parse; typically
string
or VFile
; any value accepted as x
in new VFile(x)
Syntax tree representing file
(Node
).
This example shows how parse
can be used to create a tree from a file.
1import remarkParse from 'remark-parse' 2import {unified} from 'unified' 3 4const tree = unified().use(remarkParse).parse('# Hello world!') 5 6console.log(tree)
Yields:
1{ 2 type: 'root', 3 children: [ 4 {type: 'heading', depth: 1, children: [Array], position: [Object]} 5 ], 6 position: { 7 start: {line: 1, column: 1, offset: 0}, 8 end: {line: 1, column: 15, offset: 14} 9 } 10}
processor.parser
Parser to use (Parser
, optional).
processor.process(file[, done])
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.
processor.process(file, done)
Promise<VFile> = processor.process(file?)
file
(Compatible
, optional) — file; typically
string
or VFile
; any value accepted as x
in new VFile(x)
done
(ProcessCallback
, optional) — callbackNothing if done
is given (undefined
).
Otherwise a promise, rejected with a fatal error or resolved with the
processed file (Promise<VFile>
).
The parsed, transformed, and compiled value is available at file.value
(see
note).
👉 Note: unified typically compiles by serializing: most compilers return
string
(orUint8Array
). Some compilers, such as the one configured withrehype-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
CompileResultMap
.
This example shows how process
can be used to process a file:
1import rehypeDocument from 'rehype-document' 2import rehypeFormat from 'rehype-format' 3import rehypeStringify from 'rehype-stringify' 4import remarkParse from 'remark-parse' 5import remarkRehype from 'remark-rehype' 6import {unified} from 'unified' 7 8const file = await unified() 9 .use(remarkParse) 10 .use(remarkRehype) 11 .use(rehypeDocument, {title: '👋🌍'}) 12 .use(rehypeFormat) 13 .use(rehypeStringify) 14 .process('# Hello world!') 15 16console.log(String(file))
Yields:
1<!doctype html> 2<html lang="en"> 3 <head> 4 <meta charset="utf-8"> 5 <title>👋🌍</title> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 </head> 8 <body> 9 <h1>Hello world!</h1> 10 </body> 11</html>
processor.processSync(file)
Process the given file as configured on the processor.
An error is thrown if asynchronous transforms are configured.
👉 Note:
processSync
freezes the processor if not already frozen.
👉 Note:
processSync
performs the parse, run, and stringify phases.
file
(Compatible
, optional) — file; typically
string
or VFile
; any value accepted as x
in new VFile(x)
The processed file (VFile
).
The parsed, transformed, and compiled value is available at file.value
(see
note).
👉 Note: unified typically compiles by serializing: most compilers return
string
(orUint8Array
). Some compilers, such as the one configured withrehype-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
CompileResultMap
.
This example shows how processSync
can be used to process a file, if all
transformers are synchronous.
1import rehypeDocument from 'rehype-document' 2import rehypeFormat from 'rehype-format' 3import rehypeStringify from 'rehype-stringify' 4import remarkParse from 'remark-parse' 5import remarkRehype from 'remark-rehype' 6import {unified} from 'unified' 7 8const processor = unified() 9 .use(remarkParse) 10 .use(remarkRehype) 11 .use(rehypeDocument, {title: '👋🌍'}) 12 .use(rehypeFormat) 13 .use(rehypeStringify) 14 15console.log(String(processor.processSync('# Hello world!')))
Yields:
1<!doctype html> 2<html lang="en"> 3 <head> 4 <meta charset="utf-8"> 5 <title>👋🌍</title> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 </head> 8 <body> 9 <h1>Hello world!</h1> 10 </body> 11</html>
processor.run(tree[, file][, done])
Run transformers on a syntax tree.
👉 Note:
run
freezes the processor if not already frozen.
👉 Note:
run
performs the run phase, not other phases.
processor.run(tree, done)
processor.run(tree, file, done)
Promise<Node> = processor.run(tree, file?)
tree
(Node
) — tree to transform and inspectfile
(Compatible
, optional) — file associated
with node
; any value accepted as x
in new VFile(x)
done
(RunCallback
, optional) — callbackNothing if done
is given (undefined
).
Otherwise, a promise rejected with a fatal error or resolved with the
transformed tree (Promise<Node>
).
This example shows how run
can be used to transform a tree:
1import remarkReferenceLinks from 'remark-reference-links' 2import {unified} from 'unified' 3import {u} from 'unist-builder' 4 5const tree = u('root', [ 6 u('paragraph', [ 7 u('link', {href: 'https://example.com'}, [u('text', 'Example Domain')]) 8 ]) 9]) 10 11const changedTree = await unified().use(remarkReferenceLinks).run(tree) 12 13console.log(changedTree)
Yields:
1{ 2 type: 'root', 3 children: [ 4 {type: 'paragraph', children: [Array]}, 5 {type: 'definition', identifier: '1', title: '', url: undefined} 6 ] 7}
processor.runSync(tree[, file])
Run transformers on a syntax tree.
An error is thrown if asynchronous transforms are configured.
👉 Note:
runSync
freezes the processor if not already frozen.
👉 Note:
runSync
performs the run phase, not other phases.
tree
(Node
) — tree to transform and inspectfile
(Compatible
, optional) — file associated
with node
; any value accepted as x
in new VFile(x)
Transformed tree (Node
).
processor.stringify(tree[, file])
Compile a syntax tree.
👉 Note:
stringify
freezes the processor if not already frozen.
👉 Note:
stringify
performs the stringify phase, not the run phase or other phases.
tree
(Node
) — tree to compilefile
(Compatible
, optional) — file associated
with node
; any value accepted as x
in new VFile(x)
Textual representation of the tree (Uint8Array
or string
, see note).
👉 Note: unified typically compiles by serializing: most compilers return
string
(orUint8Array
). Some compilers, such as the one configured withrehype-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
CompileResultMap
.
This example shows how stringify
can be used to serialize a syntax tree:
1import {h} from 'hastscript' 2import rehypeStringify from 'rehype-stringify' 3import {unified} from 'unified' 4 5const tree = h('h1', 'Hello world!') 6 7const document = unified().use(rehypeStringify).stringify(tree) 8 9console.log(document)
Yields:
1<h1>Hello world!</h1>
processor.use(plugin[, options])
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.
processor.use(preset?)
processor.use(list)
processor.use(plugin[, ...parameters])
preset
(Preset
) — plugins and settingslist
(PluggableList
) — list of usable thingsplugin
(Plugin
) — pluginparameters
(Array<unknown>
) — configuration for plugin
, typically a
single options objectCurrent processor (processor
).
There are many ways to pass plugins to .use()
.
This example gives an overview:
1import {unified} from 'unified' 2 3unified() 4 // Plugin with options: 5 .use(pluginA, {x: true, y: true}) 6 // Passing the same plugin again merges configuration (to `{x: true, y: false, z: true}`): 7 .use(pluginA, {y: false, z: true}) 8 // Plugins: 9 .use([pluginB, pluginC]) 10 // Two plugins, the second with options: 11 .use([pluginD, [pluginE, {}]]) 12 // Preset with plugins and settings: 13 .use({plugins: [pluginF, [pluginG, {}]], settings: {position: false}}) 14 // Settings only: 15 .use({settings: {position: false}})
CompileResultMap
Interface of known results from compilers (TypeScript type).
Normally, compilers result in text (Value
of vfile
).
When you compile to something else, such as a React node (as in,
rehype-react
), you can augment this interface to include that type.
1import type {ReactNode} from 'somewhere' 2 3declare module 'unified' { 4 interface CompileResultMap { 5 // Register a new result (value is used, key should match it). 6 ReactNode: ReactNode 7 } 8} 9 10export {} // You may not need this, but it makes sure the file is a module.
Use CompileResults
to access the values.
1interface CompileResultMap { 2 // Note: if `Value` from `VFile` is changed, this should too. 3 Uint8Array: Uint8Array 4 string: string 5}
CompileResults
Acceptable results from compilers (TypeScript type).
To register custom results, add them to
CompileResultMap
.
1type CompileResults = CompileResultMap[keyof CompileResultMap]
Compiler
A compiler handles the compiling of a syntax tree to something else (in most cases, text) (TypeScript type).
It is used in the stringify phase and called with a Node
and VFile
representation of the document to compile.
It should return the textual representation of the given tree (typically
string
).
👉 Note: unified typically compiles by serializing: most compilers return
string
(orUint8Array
). Some compilers, such as the one configured withrehype-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
CompileResultMap
.
1type Compiler< 2 Tree extends Node = Node, 3 Result extends CompileResults = CompileResults 4> = (tree: Tree, file: VFile) => Result
Data
Interface of known data that can be supported by all plugins (TypeScript type).
Typically, options can be given to a specific plugin, but sometimes it makes sense to have information shared with several plugins. For example, a list of HTML elements that are self-closing, which is needed during all phases.
To type this, do something like:
1declare module 'unified' { 2 interface Data { 3 htmlVoidElements?: Array<string> | undefined 4 } 5} 6 7export {} // You may not need this, but it makes sure the file is a module.
1interface Data { 2 settings?: Settings | undefined 3}
See Settings
for more info.
Parser
A parser handles the parsing of text to a syntax tree (TypeScript type).
It is used in the parse phase and is called with a string
and
VFile
of the document to parse.
It must return the syntax tree representation of the given file
(Node
).
1type Parser<Tree extends Node = Node> = (document: string, file: VFile) => Tree
Pluggable
Union of the different ways to add plugins and settings (TypeScript type).
1type Pluggable = 2 | Plugin<Array<any>, any, any> 3 | PluginTuple<Array<any>, any, any> 4 | Preset
See Plugin
, PluginTuple
,
and Preset
for more info.
PluggableList
List of plugins and presets (TypeScript type).
1type PluggableList = Array<Pluggable>
See Pluggable
for more info.
Plugin
Single plugin (TypeScript type).
Plugins configure the processors they are applied on in the following ways:
In practice, they are functions that can receive options and configure the
processor (this
).
👉 Note: plugins are called when the processor is frozen, not when they are applied.
1type Plugin< 2 PluginParameters extends unknown[] = [], 3 Input extends Node | string | undefined = Node, 4 Output = Input 5> = ( 6 this: Processor, 7 ...parameters: PluginParameters 8) => Input extends string // Parser. 9 ? Output extends Node | undefined 10 ? undefined | void 11 : never 12 : Output extends CompileResults // Compiler. 13 ? Input extends Node | undefined 14 ? undefined | void 15 : never 16 : // Inspect/transform. 17 | Transformer< 18 Input extends Node ? Input : Node, 19 Output extends Node ? Output : Node 20 > 21 | undefined 22 | void
See Transformer
for more info.
move.js
:
1/** 2 * @typedef Options 3 * Configuration (required). 4 * @property {string} extname 5 * File extension to use (must start with `.`). 6 */ 7 8/** @type {import('unified').Plugin<[Options]>} */ 9export function move(options) { 10 if (!options || !options.extname) { 11 throw new Error('Missing `options.extname`') 12 } 13 14 return function (_, file) { 15 if (file.extname && file.extname !== options.extname) { 16 file.extname = options.extname 17 } 18 } 19}
example.md
:
1# Hello, world!
example.js
:
1import rehypeStringify from 'rehype-stringify' 2import remarkParse from 'remark-parse' 3import remarkRehype from 'remark-rehype' 4import {read, write} from 'to-vfile' 5import {unified} from 'unified' 6import {reporter} from 'vfile-reporter' 7import {move} from './move.js' 8 9const file = await unified() 10 .use(remarkParse) 11 .use(remarkRehype) 12 .use(move, {extname: '.html'}) 13 .use(rehypeStringify) 14 .process(await read('example.md')) 15 16console.error(reporter(file)) 17await write(file) // Written to `example.html`.
Yields:
1example.md: no issues found
…and in example.html
:
1<h1>Hello, world!</h1>
PluginTuple
Tuple of a plugin and its configuration (TypeScript type).
The first item is a plugin, the rest are its parameters.
1type PluginTuple< 2 TupleParameters extends unknown[] = [], 3 Input extends Node | string | undefined = undefined, 4 Output = undefined 5> = [ 6 plugin: Plugin<TupleParameters, Input, Output>, 7 ...parameters: TupleParameters 8]
See Plugin
for more info.
Preset
Sharable configuration (TypeScript type).
They can contain plugins and settings.
plugins
(PluggableList
, optional)
— list of plugins and presetssettings
(Data
, optional)
— shared settings for parsers and compilerspreset.js
:
1import remarkCommentConfig from 'remark-comment-config' 2import remarkLicense from 'remark-license' 3import remarkPresetLintConsistent from 'remark-preset-lint-consistent' 4import remarkPresetLintRecommended from 'remark-preset-lint-recommended' 5import remarkToc from 'remark-toc' 6 7/** @type {import('unified').Preset} */ 8const preset = { 9 plugins: [ 10 remarkPresetLintRecommended, 11 remarkPresetLintConsistent, 12 remarkCommentConfig, 13 [remarkToc, {maxDepth: 3, tight: true}], 14 remarkLicense 15 ] 16 settings: {bullet: '*', emphasis: '*', fences: true}, 17} 18 19export default preset
example.md
:
1# Hello, world! 2 3_Emphasis_ and **importance**. 4 5## Table of contents 6 7## API 8 9## License
example.js
:
1import {remark} from 'remark' 2import {read, write} from 'to-vfile' 3import {reporter} from 'vfile-reporter' 4import preset from './preset.js' 5 6const file = await remark() 7 .use(preset) 8 .process(await read('example.md')) 9 10console.error(reporter(file)) 11await write(file)
Yields:
1example.md: no issues found
example.md
now contains:
1# Hello, world! 2 3*Emphasis* and **importance**. 4 5## Table of contents 6 7* [API](#api) 8* [License](#license) 9 10## API 11 12## License 13 14[MIT](license) © [Titus Wormer](https://wooorm.com)
ProcessCallback
Callback called when the process is done (TypeScript type).
Called with either an error or a result.
error
(Error
, optional)
— fatal errorfile
(VFile
, optional)
— processed fileNothing (undefined
).
This example shows how process
can be used to process a file with a callback.
1import remarkGithub from 'remark-github' 2import remarkParse from 'remark-parse' 3import remarkStringify from 'remark-stringify' 4import {unified} from 'unified' 5import {reporter} from 'vfile-reporter' 6 7unified() 8 .use(remarkParse) 9 .use(remarkGithub) 10 .use(remarkStringify) 11 .process('@unifiedjs', function (error, file) { 12 if (error) throw error 13 if (file) { 14 console.error(reporter(file)) 15 console.log(String(file)) 16 } 17 })
Yields:
1no issues found
1[**@unifiedjs**](https://github.com/unifiedjs)
Processor
Type of a processor
(TypeScript type).
RunCallback
Callback called when transformers are done (TypeScript type).
Called with either an error or results.
error
(Error
, optional)
— fatal errortree
(Node
, optional)
— transformed treefile
(VFile
, optional)
— fileNothing (undefined
).
Settings
Interface of known extra options, that can be supported by parser and compilers.
This exists so that users can use packages such as remark
, which configure
both parsers and compilers (in this case remark-parse
and
remark-stringify
), and still provide options for them.
When you make parsers or compilers, that could be packaged up together, you
should support this.data('settings')
as input and merge it with explicitly
passed options
.
Then, to type it, using remark-stringify
as an example, do something like:
1declare module 'unified' { 2 interface Settings { 3 bullet: '*' | '+' | '-' 4 // … 5 } 6} 7 8export {} // You may not need this, but it makes sure the file is a module.
1interface Settings {}
TransformCallback
Callback passed to transforms (TypeScript type).
If the signature of a transformer
accepts a third argument, the transformer
may perform asynchronous operations, and must call it.
error
(Error
, optional)
— fatal error to stop the processtree
(Node
, optional)
— new, changed, treefile
(VFile
, optional)
— new, changed, fileNothing (undefined
).
Transformer
Transformers handle syntax trees and files (TypeScript type).
They are functions that are called each time a syntax tree and file are
passed through the run phase.
When an error occurs in them (either because it’s thrown, returned,
rejected, or passed to next
), the process stops.
The run phase is handled by trough
, see its documentation for
the exact semantics of these functions.
👉 Note: you should likely ignore
next
: don’t accept it. it supports callback-style async work. But promises are likely easier to reason about.
1type Transformer< 2 Input extends Node = Node, 3 Output extends Node = Input 4> = ( 5 tree: Input, 6 file: VFile, 7 next: TransformCallback<Output> 8) => 9 | Promise<Output | undefined> 10 | Output 11 | Error 12 | undefined
This package is fully typed with TypeScript.
It exports the additional types
CompileResultMap
,
CompileResults
,
Compiler
,
Data
,
Parser
,
Pluggable
,
PluggableList
,
Plugin
,
PluginTuple
,
Preset
,
ProcessCallback
,
Processor
,
RunCallback
,
Settings
,
TransformCallback
,
and Transformer
For TypeScript to work, it is particularly important to type your plugins
correctly.
We strongly recommend using the Plugin
type with its generics and to use the
node types for the syntax trees provided by our packages (as in,
@types/hast
, @types/mdast
,
@types/nlcst
).
1/** 2 * @typedef {import('hast').Root} HastRoot 3 * @typedef {import('mdast').Root} MdastRoot 4 */ 5 6/** 7 * @typedef Options 8 * Configuration (optional). 9 * @property {boolean | null | undefined} [someField] 10 * Some option (optional). 11 */ 12 13// To type options: 14/** @type {import('unified').Plugin<[(Options | null | undefined)?]>} */ 15export function myPluginAcceptingOptions(options) { 16 const settings = options || {} 17 // `settings` is now `Options`. 18} 19 20// To type a plugin that works on a certain tree, without options: 21/** @type {import('unified').Plugin<[], MdastRoot>} */ 22export function myRemarkPlugin() { 23 return function (tree, file) { 24 // `tree` is `MdastRoot`. 25 } 26} 27 28// To type a plugin that transforms one tree into another: 29/** @type {import('unified').Plugin<[], MdastRoot, HastRoot>} */ 30export function remarkRehype() { 31 return function (tree) { 32 // `tree` is `MdastRoot`. 33 // Result must be `HastRoot`. 34 } 35} 36 37// To type a plugin that defines a parser: 38/** @type {import('unified').Plugin<[], string, MdastRoot>} */ 39export function remarkParse(options) {} 40 41// To type a plugin that defines a compiler: 42/** @type {import('unified').Plugin<[], HastRoot, string>} */ 43export function rehypeStringify(options) {}
Projects maintained by the unified collective are compatible with maintained versions of Node.js.
When we cut a new major release, we drop support for unmaintained versions of
Node.
This means we try to keep the current release line, unified@^11
, compatible
with Node.js 16.
See contributing.md
in unifiedjs/.github
for ways
to get started.
See support.md
for ways to get help.
This project has a code of conduct. By interacting with this repository, organization, or community you agree to abide by its terms.
For info on how to submit a security report, see our security policy.
Support this effort and give back by sponsoring on OpenCollective!
Vercel |
Motif |
HashiCorp |
American Express |
GitBook | |||||
Gatsby |
Netlify![]() |
Coinbase |
ThemeIsle |
Expo |
Boost Note![]() |
Markdown Space![]() |
Holloway | ||
You? |
Preliminary work for unified was done in 2014 for
retext and inspired by ware
.
Further incubation happened in remark.
The project was finally externalised in 2015 and published as unified
.
The project was authored by @wooorm.
Although unified
since moved its plugin architecture to trough
,
thanks to @calvinfo,
@ianstormtaylor, and others for their
work on ware
, as it was a huge initial inspiration.
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
no dangerous workflow patterns detected
Reason
0 existing vulnerabilities detected
Reason
license file detected
Details
Reason
security policy file detected
Details
Reason
0 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 0
Reason
Found 2/30 approved changesets -- score normalized to 0
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
project is not fuzzed
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Score
Last Scanned on 2025-06-30
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More