commonmark for PHP

CommonMark for PHP is a PHP library to parse Markdown files to HTML. It supports extended syntax, and you can define your own.

You can install it using composer:

$ composer require league/commonmark

A basic example:

use League\CommonMark\CommonMarkConverter;

$converter = new CommonMarkConverter();
echo $converter->convert("**Hello, World**")->getContent();

⚠️ Code changed a lot between version 1.x and 2.x.

Custom Converter

To create a custom convert that you can tune however you want, you can use the MarkdownConverter class.

// define your configuration
$config = [];
$environment = new Environment($config);
// define parsing rules
$environment->addExtension(new CommonMarkCoreExtension());
// define rendering rules
$environment->addRenderer(XXX::class, new XXXRenderer());
// convert
$converter = new MarkdownConverter($environment);
echo $converter->convert("**Hello, World**")->getContent();


Refer to this section of the documentation.


By default, you won't be able to parse any markdown. You can either load your own parsers, or use existing ones:

$environment->addExtension(new CommonMarkCoreExtension());
$environment->addExtension(new TaskListExtension());
$environment->addExtension(new TableExtension());

All extensions are listed and explained here.


Renderers are used to define how each element parsed is rendered.

For instance, you could define that all parsed links starting with HTTP will be red and those starting with HTTPS will be green.

➑️ See also: HtmlDecorator to only add HTML attributes.

Custom renderers

Add a custom renderer

To add a renderer, you need to define the parsed element that will be rendered using your custom renderer.

$environment->addRenderer(Heading::class, new HeadingRenderer());

See also: Link::class, Image::class, Table::class...

Custom renderer class

Each renderer implements render from NodeRendererInterface.

class HeadingRenderer implements NodeRendererInterface {
    public function render(Node $node, ChildNodeRendererInterface $childRenderer)
        // ...
        return new HtmlElement($tag, $attrs, $childRenderer->renderNodes($node->children()));

It returns a HtmlElement which is the HTML element that will be displayed. For instance, for <h1 id="toto">...</h1>, you would have:

$tag = "h1";
$attrs = ["id" => "toto"];

You should assert the type of node you're manipulating before using class-specific methods on it.

Heading::assertInstanceOf($node); // for Heading::class

Some generic methods:

// Get attributes
$attrs = $node->data->getData('attributes');
// Call render on all children nodes
$innerHtml = $childRenderer->renderNodes($node->children());


  • $node->getLevel(): get the level, such as 2 for ##


  • $node->getUrl(): get the image URL


  • $node->getUrl(): get the link URL


  • $node->getLiteral(): get the fenced code text
  • $node->getInfoWords(): get the fenced code language

Custom parser

You can create your own Markdown syntax and create a parser for it πŸš‚. You'll have to create a renderer too πŸ–ΌοΈ.

There are too categories of parsers: Inline and Block. The bold syntax **x** would be inline, while a table ("<table>") would be a block.

Block parsers are split in a StartParser that checks if your parser can parse a block and a Parser that actually parse it.

$environment->addBlockStartParser(new XXXStartParser());

⚠️ The order to add parsers to the environment is important. You may even have to add it before any extension.


Return BlockStart::none() if your parser is not supposed to parse this block. Otherwise, return BlockStart::of(...).

Use the cursor's methods to move around and find if your parser is supposed to parse this block.

class BootstrapVerticalTabStartParser implements BlockStartParserInterface
    public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
        $char = $cursor->getNextNonSpaceCharacter();
        return BlockStart::of(new XXXParser())->at($cursor);


The logic of a parser can be explained as follows:

  • tryContinue is called for you to check if the current line is still within the block

  • addLine is then called for you to parse the Markdown

  • closeBlock when you parsed all of your lines

Then, parseInlines is called. This method will allow you to parse inline symbols such as **bold** that were inside of your block.

XXXParser sample code
class XXXParser extends AbstractBlockContinueParser  implements BlockContinueParserWithInlinesInterface {
    private XXX $block;

    public function __construct(Environment $environment)
        $this->block = new XXX();

    public function getBlock(): AbstractBlock
        return $this->block;
    public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
        // return either by using the cursor methods
        // to find if you're done or not
        return BlockContinue::finished();  // done
        return BlockContinue::at($cursor); // continue

    public function parseInlines(InlineParserEngineInterface $inlineParser): void
        // Ex: store in a Paragraph the line
        // After parsing its inline elements.
        // Then store it as a child of our block
        $xxx = "some content you **parsed**";
        $p = new Paragraph();
        $inlineParser->parse($xxx, $p);
    public function addLine(string $line): void
        // each time you accept to continue
        // parsing, this method is called
        // with the line that you need to parse
        $this->block->number_of_lines++; // example
    public function closeBlock(): void
        // for instance, you may have to
        // process the last element here
    public function canHaveLazyContinuationLines(): bool
        return true;

As you'll create your own renderer, you can do anything you want, such as creating new attributes or methods in XXX.

class XXX extends AbstractBlock
    public int $number_of_lines = 0;

Here is how you can get started with the renderer:

class XXXRenderer implements NodeRendererInterface
    public function render(Node $node, ChildNodeRendererInterface $childRenderer): ?HtmlElement {
        var_dump($node->number_of_lines); // ok
        return null; // todo