Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Shortcode Plus Laravel Package

murdercode/laravel-shortcode-plus

A Laravel package for parsing shortcodes inside content, with support for attributes and custom handlers. Register your own shortcodes, then render them in Blade views or user-generated text to keep templates clean and content flexible without hardcoding markup.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and register the service provider

composer require murdercode/laravel-shortcode-plus

Laravel's package auto-discovery registers the service provider and the Shortcode facade. Publish the config file first — this is the registry you will edit most often:

php artisan vendor:publish --tag=shortcode-config

That gives you config/shortcode.php, which contains a shortcodes map of name → class and a strip_unknown flag. Check the published file rather than the README for the exact key names in the 5.x line; they have drifted between majors.

Write your first shortcode

Add an entry to the config map, then create a class that extends the package's abstract shortcode. The two methods you must implement are the name registration and the transform:

namespace App\Shortcodes;

use Murdercode\Shortcode\Shortcode;

class Callout extends Shortcode
{
    public function register(string $shortcode): string
    {
        return 'callout';
    }

    public function process($content, $shortcode): string
    {
        return view('shortcodes.callout', ['slot' => $content])->render();
    }
}
// config/shortcode.php
'shortcodes' => [
    'callout' => \App\Shortcodes\Callout::class,
],

First use case

The canonical fit is CMS-style body copy: editorial content, marketing pages, or any HTML stored in the database where a non-developer needs to drop a component into prose without touching Blade. Compile in a Blade view:

{!! Shortcode::compile($page->body) !!}

or, if the package's Blade directive is available in your version, @shortcode($page->body). Verify the abstract class signature against vendor/murdercode/laravel-shortcode-plus/src/Shortcode.php before writing anything non-trivial — that file is the real documentation, and the method names/signatures are what break between majors.

Implementation Patterns

Class-per-shortcode, config-driven registry. Resist the temptation to put logic in the config array. The registry should be a pure map; every transform lives in its own class that can be unit tested without booting the shortcode parser. This is also what makes the package's strip_unknown behaviour tolerable — you can see exactly what is and isn't registered.

Store raw, compile at render. The single biggest architectural decision. Storing compiled HTML means re-rendering every page when a shortcode's markup changes; storing raw source means you pay the parse cost on every request. Take the second option and cache the compiled output keyed on the raw body plus an updated_at timestamp.

Wrap the facade behind your own service. Shortcode::compile() is a static-ish call into a container-bound resolver, which makes it awkward to swap in tests and impossible to add instrumentation. A thin ShortcodeRenderer wrapper gives you a single place to add caching, logging of unknown/stripped tags, and an escape hatch if the package is abandoned.

Enclosing vs self-closing. Use enclosing shortcodes ([callout]…[/callout]) for content-wrapping components where the author provides the inner text, and self-closing for parameter-driven embeds. Keep the $content handling in process() defensive — it can be empty, and it is user-supplied.

Attributes and validation. Shortcodes are a user-facing input surface. Anything parsed out of the tag (attributes, IDs, slugs) is untrusted: validate against a whitelist and fail closed. For a [video id="..."] shortcode, resolve the ID against known records rather than interpolating it into a URL.

Integration points beyond page bodies. The same compile step is useful for:

  • Mail templates where marketing wants to embed a CTA without a deploy.
  • Markdown pipelines — compile before Markdown so shortcodes can produce block-level HTML, or after if you want shortcodes to work inside code fences being preserved. Pick one and document it, because the two produce visibly different output.
  • API responses for a headless front end, where the SPA can't render the component server-side.

Testing. Unit test each shortcode class directly — new Callout()->process('hi', 'callout') — and use a single feature test asserting the facade's end-to-end output for one representative document. That keeps the bulk of the suite decoupled from the package's parser internals.

Gotchas and Tips

Output is raw HTML. Whatever process() returns goes to the browser unescaped. Escape the $content you interpolate, and never pass user attributes through without sanitising. This is the failure mode that turns a CMS feature into a stored-XSS vector.

Config caching. After editing config/shortcode.php in a deployed environment, php artisan config:cache will serve the stale registry and new shortcodes will silently render as plain text (or vanish, depending on strip_unknown). Add config-cache rebuild to the deploy script, not to muscle memory.

strip_unknown semantics. With stripping on, an unregistered [foo] disappears entirely — which hides typos from authors and makes the failure look like data loss. With it off, the literal brackets leak into the rendered page. Either way, log unknown tags during compile (or scan content in a scheduled job) so editorial mistakes surface as a report rather than a mystery.

Whitespace and case sensitivity. Shortcode names are matched literally. [Callout], [callout ], and [callout\n] may not all resolve. Write a lint command that scans stored content for bracket patterns and reports near-misses; it pays for itself the first week.

Nesting order. Nested shortcodes are processed in a defined order that is easy to get wrong when you introduce a shortcode that emits another shortcode's syntax. If a shortcode's output legitimately contains bracket syntax, escape it or you'll get double-processing.

Performance. Compilation is regex work over the whole body, so it scales with content length, not with the number of shortcodes. For a listing page rendering 50 articles, compile once per article and cache; don't compile inside a loop that also re-renders on every request.

Debugging. When a shortcode doesn't fire, check in this order: is it in the config map, is config cached, does register() return exactly the string used in the content, and does the class resolve from the container (constructor dependencies are a common break). Dumping Shortcode::compile('[callout]x[/callout]') in tinker isolates parser problems from view problems in one step.

Extension points. Overriding the published config, subclassing the parser or the abstract shortcode, and the Blade directive are the seams available. Prefer subclassing your own shortcode classes over extending the package internals — the parser's protected methods are the least stable surface in a 5.x line.

On v5.3.1. The release notes carry only a compare link, so there is no documented behaviour change. It's a patch on the 5.3 branch: no migration expected, no config or signature changes implied. Pin to ^5.3.1 and read the diff on GitHub if you were tracking a specific bug. Treat any future major as a full re-read of the abstract Shortcode class, since that's where breakage lands.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core