Skip to content

Props helpers

The backstage/props package exposes a props() helper backed by the backstage.props container binding. It is the parallel to core's stage() helper, meant for project-specific UX additions that should not live in core.

php
props();          // Backstage\Props\Helpers\Support\Helpers
props()->ping();  // example helper exposed by props ('pong')

Like stage(), the props() helper also accepts a binding name (with optional dot-notation), e.g. props('something.value'), returning null when the binding is not registered.

Use stage() for everything provided by core; reach for props() only when you have registered your own helpers in the props package.

Available helpers

Out of the box, props() exposes the following helpers:

Determine whether a link points outside the current site. Relative URLs are always internal; absolute URLs are compared against the current host (falling back to config('app.url')), ignoring a www. prefix and treating subdomains as internal. Returns false for an empty link.

php
props()->isExternalLink('https://example.com');   // true (on a different host)
props()->isExternalLink('/about');                 // false — relative URL
props()->isExternalLink('https://www.mysite.test'); // false — same host as the request
blade
<a href="{{ $url }}" @if(props()->isExternalLink($url)) target="_blank" rel="noopener noreferrer" @endif>
    {{ $label }}
</a>

formatCallablePhoneNumber()

Normalise a phone number for use in a tel: link: strips whitespace, converts a leading + country code to 00, and removes a (0) trunk prefix. Returns an empty string for a blank input.

php
props()->formatCallablePhoneNumber('+31 (0)6 1234 5678'); // '0031612345678'
props()->formatCallablePhoneNumber(null);                  // ''
blade
<a href="tel:{{ props()->formatCallablePhoneNumber($phone) }}">{{ $phone }}</a>

Adding your own helpers

backstage/props is the package front-enders own. Add reusable Blade/template helpers here — never in backstage/cms. Every new helper is two changes inside the props package:

  1. A utility trait under Helpers/Support/Utilities/ containing the method.
  2. The matching method signature on the PropsHelpers contract.

Both are required: the trait makes the helper callable, the contract keeps props() typed and signals to other developers what is available.

1. Create a utility trait

php
// src/Helpers/Support/Utilities/MenuUtility.php
namespace Backstage\Props\Helpers\Support\Utilities;

use Backstage\Models\Menu;

trait MenuUtility
{
    public function menu(string $slug): ?Menu
    {
        return Menu::where('slug', $slug)->first();
    }
}

The trait inherits the $app container property from DefaultUtility, so $this->app->getLocale() and $this->retrieveBinding('site') are available.

2. Update the contract

The contract is the source of truth for what props() can do. Add the method signature:

php
// src/Helpers/Contracts/PropsHelpers.php
namespace Backstage\Props\Helpers\Contracts;

use Backstage\Models\Menu;

interface PropsHelpers
{
    public function ping(): string;

    public function menu(string $slug): ?Menu;
}

3. Compose the trait into Helpers

php
// src/Helpers/Support/Helpers.php
class Helpers implements PropsHelpers
{
    use Utilities\DefaultUtility;
    use Utilities\PingUtility;
    use Utilities\MenuUtility; // ← new
}

The helper is now available everywhere props() resolves:

blade
{{ props()->menu('main')?->title }}
php
$menu = props()->menu('footer');

Conventions

  • One trait per topicMenuUtility, NavigationUtility, BreadcrumbUtility etc. Keep Helpers a one-line composition.
  • Always update the contract. Skipping it works at runtime but breaks IDE completion and static analysis, and makes it harder for the next front-ender to see what is available.
  • Use $this->retrieveBinding('site') (not app('site')) when a helper needs a request-bound model, so the "outside of a content response" exception is thrown consistently.
  • Never add helpers to backstage/cms. Core's CoreHelpers contract is reserved for framework-level lookups (site(), content()); everything UX-shaped belongs in props.