# Use Laravel URL Helpers inside Markdown

**Package:** CommonMark Routes | **Version:** 2 | **URL:** https://mozex.dev/docs/commonmark-routes/v2

---

A [league/commonmark](https://github.com/thephpleague/commonmark) extension that lets you use `route()`, `url()`, and `asset()` inside your Markdown content. Write links and images using the same Laravel helpers you already use in Blade, and they'll resolve to real URLs when the Markdown is converted.

> **Note:** Helper arguments are parsed, not executed. Only literal values get through, so Markdown can't run PHP. See [Helper Arguments](#helper-arguments).

## Installation

> **Requires [PHP 8.2+](https://php.net/releases/)** - see [all version requirements](https://mozex.dev/docs/commonmark-routes/v2/requirements)

Install the package via Composer:

```bash
composer require mozex/commonmark-routes
```

## Upgrading from 1.x

Version 1 ran every helper call through PHP's `eval()`. Any Markdown you converted could execute arbitrary code, which is why the old README told you never to process user-submitted content. Version 2 parses the arguments instead and accepts literal values only.

Ordinary usage is unaffected. `route('product', 3)`, `url('docs/api')`, and `route('home', ['id' => 'features'])` all behave exactly as before.

What breaks is anything that isn't a literal:

```markdown
[Docs](url('docs/' . $section))
[Docs](url(config('app.docs_path')))
[Logo](asset(strtolower('Logo.png')))
```

Those now throw `Mozex\CommonMarkRoutes\Exceptions\InvalidHelperArgumentsException`. Work the value out in PHP and pass the finished Markdown to the converter.

Three smaller changes, none of which raise an error, so check your output if any apply:

- Link text that mixes prose with a helper keeps the prose now. `[Go to route('home') now](route('home'))` used to render as `<a href="…">https://domain.com</a>`, because version 1 replaced the whole text. It now renders as `<a href="…">Go to https://domain.com now</a>`. Text that was only a helper call, like `[route('home')](route('home'))`, is unaffected.
- `RoutesExtension` no longer implements `League\Config\ConfigurationAwareInterface`, and `setConfiguration()` is gone with it. The injected config was never read. This only matters if you subclassed the extension and touched `$this->configuration`.
- Helper calls inside fenced code blocks and inline code used to resolve. They don't any more. If you were relying on that, move the content out of the code block.

## Usage

Register the extension with your CommonMark environment, then use `route()`, `url()`, or `asset()` in place of URLs in your Markdown.

```php
use League\CommonMark\CommonMarkConverter;
use Mozex\CommonMarkRoutes\RoutesExtension;

$converter = new CommonMarkConverter();
$converter->getEnvironment()->addExtension(new RoutesExtension());
```

### Links

The `route()` helper works exactly the way it does in your PHP code. Named routes, parameters, query strings, relative URLs:

```php
echo $converter->convert("[Home](route('home'))");
// <p><a href="https://domain.com">Home</a></p>

echo $converter->convert("[Product](route('product', 3))");
// <p><a href="https://domain.com/product/3">Product</a></p>

echo $converter->convert("[Features](route('home', ['id' => 'features']))");
// <p><a href="https://domain.com?id=features">Features</a></p>

echo $converter->convert("[Home](route('home', absolute: false))");
// <p><a href="/">Home</a></p>
```

The `url()` helper generates URLs from plain paths:

```php
echo $converter->convert("[About](url('about'))");
// <p><a href="https://domain.com/about">About</a></p>

echo $converter->convert("[Docs](url('docs/getting-started'))");
// <p><a href="https://domain.com/docs/getting-started">Docs</a></p>
```

The `asset()` helper resolves static file paths through Laravel's asset pipeline. This is especially useful in environments like [Laravel Vapor](https://vapor.laravel.com) where assets are served from S3 or CloudFront and relative paths won't work:

```php
echo $converter->convert("[Download PDF](asset('files/doc.pdf'))");
// <p><a href="https://domain.com/files/doc.pdf">Download PDF</a></p>
```

Helpers resolve in the link text too, wherever they appear:

```php
echo $converter->convert("[route('home')](route('home'))");
// <p><a href="https://domain.com">https://domain.com</a></p>

echo $converter->convert("[Go to route('home') now](route('home'))");
// <p><a href="https://domain.com">Go to https://domain.com now</a></p>
```

Angle brackets work too, which can help with complex arguments:

```php
echo $converter->convert("[Home](<route('home', absolute: false)>)");
// <p><a href="/">Home</a></p>
```

You can freely mix helpers with regular Markdown links in the same document:

```php
echo $converter->convert("[Home](route('home')) | [Docs](url('docs')) | [Google](https://google.com)");
// <p><a href="https://domain.com">Home</a> | <a href="https://domain.com/docs">Docs</a> | <a href="https://google.com">Google</a></p>
```

### Link Titles

Titles survive the rewrite. All three CommonMark forms work, on links and images alike:

```php
echo $converter->convert("[Home](route('home') \"Go home\")");
// <p><a href="https://domain.com" title="Go home">Home</a></p>

echo $converter->convert("![Logo](https://raw.githubusercontent.com/mozex/commonmark-routes/main/asset('logo.png') 'Our logo')");
// <p><img src="https://domain.com/logo.png" alt="Logo" title="Our logo" /></p>
```

### Images

Image syntax works the same way. Put a helper inside `![alt](https://raw.githubusercontent.com/mozex/commonmark-routes/main/...)` and it resolves just like links do:

```php
echo $converter->convert("![Logo](https://raw.githubusercontent.com/mozex/commonmark-routes/main/asset('images/logo.png'))");
// <p><img src="https://domain.com/images/logo.png" alt="Logo" /></p>

echo $converter->convert("![Banner](https://raw.githubusercontent.com/mozex/commonmark-routes/main/url('images/banner.jpg'))");
// <p><img src="https://domain.com/images/banner.jpg" alt="Banner" /></p>

echo $converter->convert("![Product](https://raw.githubusercontent.com/mozex/commonmark-routes/main/route('product', 3))");
// <p><img src="https://domain.com/product/3" alt="Product" /></p>
```

The `asset()` helper is the most common choice for images. If you're on Vapor or any setup that serves assets from a CDN, `asset()` gives you the correct absolute URL instead of a broken relative path.

Regular images without helpers pass through untouched:

```php
echo $converter->convert("![Photo](https://example.com/photo.jpg)");
// <p><img src="https://example.com/photo.jpg" alt="Photo" /></p>
```

For more details on CommonMark extensions and environments, check the [CommonMark documentation](https://commonmark.thephpleague.com/2.4/basic-usage/).

### Helper Arguments

Write arguments the way you'd write them in PHP. These are the values the parser accepts:

- Strings, single or double quoted: `route('product')`, `url("about")`
- Integers and floats: `route('product', 3)`
- Booleans and null: `route('home', [], false)`
- Arrays, including nested ones: `route('search', ['filters' => ['tag' => 'php']])`
- Named arguments: `route('home', absolute: false)`

Double-quoted strings don't interpolate. `url("costs/$100")` gives you a literal `$100` in the path.

Anything else throws `Mozex\CommonMarkRoutes\Exceptions\InvalidHelperArgumentsException` with the offending source in the message. That covers variables, function calls, concatenation, and constants. It's what stops Markdown from reaching PHP.

### Code Blocks

Helpers inside fenced code blocks and inline code are left alone, so you can document the syntax without it resolving on you:

````markdown
```php
[Home](route('home'))
```

Inline `[Home](route('home'))` stays put too.
````

Both render as literal text. This README goes through the extension unchanged.

One gap worth knowing about: indented code blocks (the four-space kind) aren't protected. Telling them apart from nested list content needs a full block parse, and guessing wrong would silently break links inside lists. Use fenced blocks when the content has helper calls in it.

### Spatie Laravel Markdown

If you're using the [Laravel Markdown](https://github.com/spatie/laravel-markdown/) package by Spatie, register the extension in `config/markdown.php`:

```php
/*
 * These extensions should be added to the markdown environment. A valid
 * extension implements League\CommonMark\Extension\ExtensionInterface
 *
 * More info: https://commonmark.thephpleague.com/2.4/extensions/overview/
 */
'extensions' => [
    Mozex\CommonMarkRoutes\RoutesExtension::class,
],
```

---

## Table of Contents

- [Quick Start](https://mozex.dev/docs/commonmark-routes/v2)
- [AI Integration](https://mozex.dev/docs/commonmark-routes/v2/ai-integration)
- [Support Us](https://mozex.dev/docs/commonmark-routes/v2/support-us)
- [Requirements](https://mozex.dev/docs/commonmark-routes/v2/requirements)
- [Changelog](https://mozex.dev/docs/commonmark-routes/v2/changelog)
- [Contributing](https://mozex.dev/docs/commonmark-routes/v2/contributing)
- [Questions & Issues](https://mozex.dev/docs/commonmark-routes/v2/questions-and-issues)
- [About Mozex](https://mozex.dev/docs/commonmark-routes/v2/about)