PHPackages                             contentful/rich-text - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. contentful/rich-text

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

contentful/rich-text
====================

Utilities for the Contentful Rich Text

4.0.3(6mo ago)123.3M—6.7%10[1 issues](https://github.com/contentful/rich-text.php/issues)[1 PRs](https://github.com/contentful/rich-text.php/pulls)3MITPHPPHP ^8.0CI passing

Since Sep 13Pushed 1mo ago9 watchersCompare

[ Source](https://github.com/contentful/rich-text.php)[ Packagist](https://packagist.org/packages/contentful/rich-text)[ RSS](/packages/contentful-rich-text/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (7)Versions (32)Used By (3)

rich-text.php
=============

[](#rich-textphp)

[![Packagist](https://camo.githubusercontent.com/c4875aa9c47c0145aea4852e1640f5b72c8e1b0831567647870b2e338013da6e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f636f6e74656e7466756c2f726963682d746578742e7376673f7374796c653d666f722d7468652d6261646765)](https://packagist.org/packages/contentful/rich-text)[![PHP minimum version](https://camo.githubusercontent.com/89e8ed72629ec75d228f485bcd5e654a35a80e5c1920bc5bfc41004e50125c65/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f636f6e74656e7466756c2f726963682d746578742e7376673f7374796c653d666f722d7468652d6261646765)](https://packagist.org/packages/contentful/rich-text)[![CircleCI](https://camo.githubusercontent.com/77a9fcabb20f72c1ae97a4cbabcf9210d0141cf6531ece48eebb52b20b98088b/68747470733a2f2f696d672e736869656c64732e696f2f636972636c6563692f70726f6a6563742f6769746875622f636f6e74656e7466756c2f726963682d746578742e7068702f6d61737465722e7376673f7374796c653d666f722d7468652d6261646765)](https://circleci.com/gh/contentful/rich-text.php)[![License](https://camo.githubusercontent.com/10c9884c9ff830292d2e3acf3207fc0779c4ff6f63e129ec1335f9bf2dd0f30e/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f636f6e74656e7466756c2f726963682d746578742e7068702e7376673f7374796c653d666f722d7468652d6261646765)](https://packagist.org/packages/contentful/rich-text.php)

> This library is built to help you with the parsing and rendering of the [rich text](https://www.contentful.com/developers/docs/concepts/rich-text/) field type in Contentful. It requires PHP 7.2 and up or PHP 8.0 and up.

Setup
-----

[](#setup)

Add this package to your application by using [Composer](https://getcomposer.org/) and executing the following command:

```
composer require contentful/rich-text
```

Then, if you haven't already, include the Composer autoloader:

```
require_once 'vendor/autoload.php';
```

### Parsing

[](#parsing)

The method `Contentful\RichText\Parser::parseLocalized(array $data, string|null $locale)` accepts a valid, unserialized rich text data structure, and returns an object which implements `Contentful\RichText\Node\NodeInterface`. You will also need a link resolver to instanciate the parser.

```
$parser = new Contentful\RichText\Parser($linkResolver);

// Fetch some data from an entry field from Contentful

/** @var Contentful\RichText\Node\NodeInterface $node */
$node = $parser->parseLocalized($data, $locale);
```

The parser will also automatically resolve any linked assets and entries. To do this correctly, you will need to supply the current locale - otherwise, the parser will resolve the links in the default locale for the given space.

Depending of which type of node it actually is, the hierarchy can be navigated using getter methods. Please refer to the [full list of available nodes](https://github.com/contentful/rich-text.php/tree/master/src/Node) for a complete reference.

### Rendering

[](#rendering)

The main purpose of this library is to provide an automated way of rendering nodes. The simplest setup involves just creating an instance of the `Contentful\RichText\Renderer` class:

```
$renderer = new Contentful\RichText\Renderer();

$output = $renderer->render($node);
```

The library provides defaults for all types of supported nodes. However, it is likely that you will need to override some of these defaults, in order to customize the output. To do this, you will create `NodeRenderer` classes, which implement the `Contentful\RichText\NodeRenderer\NodeRendererInterface` interface:

```
namespace Contentful\RichText\NodeRenderer;

use Contentful\RichText\Node\NodeInterface;
use Contentful\RichText\RendererInterface;

/**
 * NodeRendererInterface.
 *
 * A class implementing this interface is responsible for
 * turning a limited subset of objects implementing NodeInterface
 * into a string.
 *
 * The node renderer makes the support for a certain node explicit by
 * implementing the "supports" method.
 *
 * The node renderer can also throw an exception during rendering if
 * the given node is not supported.
 */
interface NodeRendererInterface
{
    /**
     * Returns whether the current renderer
     * supports rendering the given node.
     *
     * @param NodeInterface $node The node which will be tested
     *
     * @return bool
     */
    public function supports(NodeInterface $node): bool;

    /**
     * Renders a node into a string.
     *
     * @param RendererInterface $renderer The generic renderer object, which is used for
     *                                    delegating rendering of nested nodes (such as ListItem in lists)
     * @param NodeInterface     $node     The node which must be rendered
     * @param array             $context  Optionally, extra context variables (useful with custom node renderers)
     *
     * @throws \InvalidArgumentException when the given $node is not supported
     *
     * @return string
     */
    public function render(RendererInterface $renderer, NodeInterface $node, array $context = []): string;
}
```

For instance, if you want to add a class to all `h1` tags, you will have something similar to this:

```
use Contentful\RichText\NodeRenderer\NodeRendererInterface;
use Contentful\RichText\Node\NodeInterface;
use Contentful\RichText\Node\Heading1;
use Contentful\RichText\RendererInterface;

class CustomHeading1 implements NodeRendererInterface
{
    public function supports(NodeInterface $node): bool
    {
        return $node instanceof Heading1;
    }

    public function render(RendererInterface $renderer, NodeInterface $node, array $context = []): string
    {
        return ''.$renderer->renderCollection($node->getContent()).'';
    }
}
```

Finally, you will need to tell the main renderer to use your custom node renderer:

```
$renderer->pushNodeRenderer(new CustomHeading1());
```

Now all instances of `Heading1` node will be rendered using the custom node renderer. You can implement any sort of logic in the `supports` method, and since only an interface is required, you can inject any sort of dependency in the constructor. This is done, for instance, for allowing the use of templating engines such as Twig or Plates:

```
use Twig\Environment;
use Contentful\RichText\NodeRenderer\NodeRendererInterface;
use Contentful\RichText\Node\NodeInterface;
use Contentful\RichText\Node\Heading1;
use Contentful\RichText\RendererInterface;

class TwigCustomHeading1 implements NodeRendererInterface
{
    /**
     * @var Environment
     */
    private $twig;

    public function __construct(Environment $twig)
    {
        $this->twig = $twig;
    }

    public function supports(NodeInterface $node): bool
    {
        return $node instanceof Heading1;
    }

    public function render(RendererInterface $renderer, NodeInterface $node, array $context = []): string
    {
        $context['node'] = $node;

        return $this->twig->render('heading1.html.twig', $context);
    }
}
```

This library provides out-of-the-box support for Twig and Plates, which allow you to call `RenderInterface::render()` and `RenderInterface::renderCollection()` methods from a template. To enable the appropriate extension, just let the Twig environment or Plates engine know about it as described below.

### Twig integration

[](#twig-integration)

Setup:

```
$renderer = new Contentful\RichText\Renderer();

// Register the Twig extension, which will provide functions
// rich_text_render() and rich_text_render_collection()
// in a Twig template
$extension = new Contentful\RichText\Bridge\TwigExtension($renderer);

/** @var Twig\Environment $twig */
$twig->addExtension($extension);

// Finally, tell the main renderer about your custom node renderer
$customTwigHeading1NodeRenderer = new TwigCustomHeading1($twig);
$renderer->pushNodeRenderer($customTwigHeading1NodeRenderer);
```

Template:

```
{{ rich_text_render_collection(node.content) }}
```

For an example implementation of a Twig-based rendering process, check the [test node renderer](https://github.com/contentful/rich-text.php/blob/master/tests/Implementation/TwigNodeRenderer.php) and the [complete integration test](https://github.com/contentful/rich-text.php/blob/master/tests/Integration/TwigNodeRendererTest.php).

### Plates integration

[](#plates-integration)

Setup:

```
$renderer = new \Contentful\RichText\Renderer();

// Register the Plates extension, which will provide functions
// $this->richTextRender() and $this->richTextRenderCollection()
// in a Plates template
$extension = new \Contentful\RichText\Bridge\PlatesExtension($renderer);

/** @var League\Plates\Engine $plates */
$plates->loadExtension($extension);

// Finally, tell the main renderer about your custom node renderer
$customPlatesHeading1NodeRenderer = new PlatesCustomHeading1($plates);
$renderer->pushNodeRenderer($customPlatesHeading1NodeRenderer);
```

Template:

```
// The function will output HTML, so remember *not* to escape it using $this->e()

```

For an example implementation of a Plates-based rendering process, check the [test node renderer](https://github.com/contentful/rich-text.php/blob/master/tests/Implementation/PlatesNodeRenderer.php) and the [complete integration test](https://github.com/contentful/rich-text.php/blob/master/tests/Integration/PlatesNodeRendererTest.php).

How to avoid having the main renderer throw an exception on unknown nodes
-------------------------------------------------------------------------

[](#how-to-avoid-having-the-main-renderer-throw-an-exception-on-unknown-nodes)

The default renderer behavior for when it does not find an appropriate node renderer is to throw an exception. To avoid this, you must set it up to use a special catch-all node renderer:

```
$renderer = new Contentful\RichText\Renderer();
$renderer->appendNodeRenderer(new Contentful\RichText\NodeRenderer\CatchAll());
```

The special `Contentful\RichText\NodeRenderer\CatchAll` node renderer will return an empty string regardless of the node type. It's important to use the `appendNodeRenderer` instead of the usual `pushNodeRenderer` method to make this special node renderer have the lowest priority, therefore avoiding it intercepting regular node renderers.

Glossary
--------

[](#glossary)

NameInterfaceDescriptionNode`Contentful\RichText\Node\NodeInterface`The PHP representation of a rich text nodeRenderer`Contentful\RichText\RendererInterface`A class which accepts all sorts of nodes, and then delegates rendering to the appropriate node rendererNode renderer`Contentful\RichText\NodeRenderer\NodeRendererInterface`A class whose purpose is to be able to render a specific type of nodeParser`Contentful\RichText\ParserInterface`A class that's responsible for turning an array of unserialized JSON data into a tree of node objectsAbout Contentful
----------------

[](#about-contentful)

[Contentful](https://www.contentful.com) is a content management platform for web applications, mobile apps and connected devices. It allows you to create, edit &amp; manage content in the cloud and publish it anywhere via powerful API. Contentful offers tools for managing editorial teams and enabling cooperation between organizations.

License
-------

[](#license)

Copyright (c) 2015-2019 Contentful GmbH. Code released under the MIT license. See [LICENSE](LICENSE) for further details.

###  Health Score

63

—

FairBetter than 99% of packages

Maintenance79

Regular maintenance activity

Popularity52

Moderate usage in the ecosystem

Community30

Small or concentrated contributor base

Maturity78

Established project with proven stability

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~124 days

Recently: every ~304 days

Total

22

Last Release

182d ago

Major Versions

1.2.1 → 2.0.02020-02-19

2.0.0 → 3.0.02020-03-03

3.4.0 → 4.0.02022-12-22

PHP version history (4 changes)1.0.0-beta1PHP ^7.0

3.0.0PHP ^7.2

3.2.0PHP ^7.2|^8.0

4.0.0PHP ^8.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/6caf49052fd43966f7e5c1a6e78e4a9830f9dd014470be9f5da0736d141c0ea5?d=identicon)[mariobodemann](/maintainers/mariobodemann)

![](https://www.gravatar.com/avatar/c0d178c1c40db8c7e92e9fe71e0f2ef461d683679ae69275adf6661c8f3b8b67?d=identicon)[contentful](/maintainers/contentful)

---

Top Contributors

[![dborsatto](https://avatars.githubusercontent.com/u/94651?v=4)](https://github.com/dborsatto "dborsatto (74 commits)")[![Sebb767](https://avatars.githubusercontent.com/u/4084001?v=4)](https://github.com/Sebb767 "Sebb767 (45 commits)")[![pgrigoruta](https://avatars.githubusercontent.com/u/1782518?v=4)](https://github.com/pgrigoruta "pgrigoruta (26 commits)")[![e1himself](https://avatars.githubusercontent.com/u/370680?v=4)](https://github.com/e1himself "e1himself (5 commits)")[![ismailatkurt](https://avatars.githubusercontent.com/u/2597406?v=4)](https://github.com/ismailatkurt "ismailatkurt (3 commits)")[![jjolton-contentful](https://avatars.githubusercontent.com/u/145477871?v=4)](https://github.com/jjolton-contentful "jjolton-contentful (3 commits)")[![matthew-contentful](https://avatars.githubusercontent.com/u/52446913?v=4)](https://github.com/matthew-contentful "matthew-contentful (3 commits)")[![michaelphamcf](https://avatars.githubusercontent.com/u/211397923?v=4)](https://github.com/michaelphamcf "michaelphamcf (2 commits)")[![t-col](https://avatars.githubusercontent.com/u/104802020?v=4)](https://github.com/t-col "t-col (2 commits)")[![whitelisab](https://avatars.githubusercontent.com/u/62958907?v=4)](https://github.com/whitelisab "whitelisab (2 commits)")[![forged-request](https://avatars.githubusercontent.com/u/104775415?v=4)](https://github.com/forged-request "forged-request (1 commits)")[![yadakhov](https://avatars.githubusercontent.com/u/146636?v=4)](https://github.com/yadakhov "yadakhov (1 commits)")[![odiffey](https://avatars.githubusercontent.com/u/25455673?v=4)](https://github.com/odiffey "odiffey (1 commits)")[![mariobodemann](https://avatars.githubusercontent.com/u/1162562?v=4)](https://github.com/mariobodemann "mariobodemann (1 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/contentful-rich-text/health.svg)

```
[![Health](https://phpackages.com/badges/contentful-rich-text/health.svg)](https://phpackages.com/packages/contentful-rich-text)
```

###  Alternatives

[mattiasgeniar/php-percentages

A clean interface to calculate percentages in PHP: changes between values, percentage increases, partial values, ...

201321.7k](/packages/mattiasgeniar-php-percentages)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
