contentful/rich-text
PHP library for parsing and rendering Contentful Rich Text fields. Parses localized rich text JSON into node objects, resolves linked assets/entries via a link resolver, and renders nodes to output with a simple Renderer API. Requires PHP 7.2+ / 8.0+.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require contentful/rich-text
Requires PHP 7.2+ or PHP 8.0+.
Basic Parsing:
use Contentful\RichText\Parser;
use Contentful\RichText\LinkResolver\LinkResolverInterface;
// Create a link resolver (e.g., for resolving Contentful entries/assets)
$linkResolver = new MyCustomLinkResolver();
// Parse rich text data (e.g., from a Contentful entry)
$parser = new Parser($linkResolver);
$node = $parser->parseLocalized($richTextData, 'en-US'); // Always use parseLocalized()
Basic Rendering:
use Contentful\RichText\Renderer;
$renderer = new Renderer();
$html = $renderer->render($node);
// In a Laravel controller or service
public function showEntry(Entry $entry)
{
$richTextData = $entry->getField('body'); // Assume 'body' is a rich text field
$linkResolver = new ContentfulLinkResolver($entry->getSpaceId());
$parser = new Parser($linkResolver);
$node = $parser->parseLocalized($richTextData, app()->getLocale());
$renderer = new Renderer();
$html = $renderer->render($node);
return view('entry.show', ['content' => $html]);
}
parseLocalized() with a custom LinkResolverInterface to resolve embedded entries/assets.// Example: Resolve links using Laravel's service container
$linkResolver = app()->make(ContentfulLinkResolver::class);
$parser = new Parser($linkResolver);
// Example: Custom renderer for headings
class CustomHeadingRenderer implements NodeRendererInterface
{
public function supports(NodeInterface $node): bool
{
return $node instanceof Heading1 || $node instanceof Heading2;
}
public function render(RendererInterface $renderer, NodeInterface $node, array $context = []): string
{
$tag = $node instanceof Heading1 ? 'h1' : 'h2';
return "<{$tag} class=\"content-heading\">" . $renderer->renderCollection($node->getContent()) . "</{$tag}>";
}
}
// Register with the renderer
$renderer->pushNodeRenderer(new CustomHeadingRenderer());
TwigExtension or PlatesExtension for templating.
// Twig example
$renderer = new Renderer();
$twig->addExtension(new \Contentful\RichText\Bridge\TwigExtension($renderer));
// Blade example (via Plates)
$plates = new Plates();
$plates->loadExtension(new \Contentful\RichText\Bridge\PlatesExtension($renderer));
{# Twig #}
{{ rich_text_render(node) }}
{# Blade (via Plates) #}
{!! $this->richTextRender($node) !!}
$renderer->enableEmbeddedImageRenderer(true);
// Resolve embedded assets using Laravel's Storage or Filesystem
$linkResolver = new ContentfulLinkResolver(
app('filesystem'),
config('contentful.space_id')
);
$nodes = $parser->parseCollectionLocalized($richTextData, 'en-US');
$html = $renderer->renderCollection($nodes);
// app/Providers/ContentfulServiceProvider.php
public function register()
{
$this->app->singleton(Parser::class, function ($app) {
return new Parser($app->make(LinkResolverInterface::class));
});
$this->app->singleton(Renderer::class, function ($app) {
$renderer = new Renderer();
$renderer->pushNodeRenderer(new CustomHeadingRenderer());
return $renderer;
});
}
Locale Mismatch:
parseLocalized() can cause embedded entries/assets to resolve in the wrong locale.$node = $parser->parseLocalized($data, app()->getLocale());
Missing CatchAll Renderer:
$renderer->appendNodeRenderer(new \Contentful\RichText\NodeRenderer\CatchAll());
appendNodeRenderer() (not pushNodeRenderer()) to ensure CatchAll has the lowest priority.Embedded Asset Rendering:
$renderer->enableEmbeddedImageRenderer(true);
PHP Version Compatibility:
Nested Node Rendering:
$renderer->renderCollection() for nested nodes:
// Wrong: Skipping nested nodes
return "<div>{$node->getContent()}</div>";
// Correct: Delegating to the renderer
return "<div>" . $renderer->renderCollection($node->getContent()) . "</div>";
Breaking Changes in v4.0.0:
parse() and parseCollection() are deprecated. Use parseLocalized() and parseCollectionLocalized() instead.Inspect Nodes:
var_dump($node) or dd($node) to inspect the parsed node structure. Each node type (e.g., Heading1, EmbeddedEntryBlock) has specific methods like getContent(), getNodeType(), etc.Check Renderer Order:
pushNodeRenderer() for high-priority overrides and appendNodeRenderer() for low-priority fallbacks.Locale-Specific Issues:
LinkResolver is correctly configured.parseLocalized() matches the content's locale.Performance:
$cacheKey = 'rich_text_' . md5(serialize($data) . $locale);
return Cache::remember($cacheKey, now()->addHours(1), function () use ($parser, $data, $locale) {
return $parser->parseLocalized($data, $locale);
});
Custom Node Types:
CustomBlock) by:
NodeInterface.NodeRendererInterface for it.Renderer.Link Resolver:
LinkResolverInterface to customize how embedded entries/assets are resolved. Example:
class ContentfulLinkResolver implements LinkResolverInterface
{
public function resolve(string $id, string $locale, string $type): ?array
{
// Fetch from Contentful API or cache
return $this->contentfulClient->getEntry($id, $locale);
}
}
Templating Engines:
class BladeExtension
{
public function __construct(private Renderer $renderer) {}
public function render(Node
How can I help you explore Laravel packages today?