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 Blade On Demand Laravel Package

protonemedia/laravel-blade-on-demand

protonemedia/laravel-blade-on-demand lets you compile and render Blade templates on the fly—including strings and dynamically generated views—without creating view files. Ideal for user-defined templates, CMS content, and email builders, with caching for performance.

View on GitHub
Deep Wiki
Context7

Getting Started

What it does: gives you a Blade compiler you can feed a template string instead of a view file. The template is compiled and evaluated in memory (compiled output is keyed/hashed rather than written as a normal view), so you can render Blade you pulled out of a database column, a CMS field, a tenant theme table, or an API payload.

Minimal steps

  1. composer require protonemedia/laravel-blade-on-demand — check composer.json on the repo for the PHP/Laravel constraint before you commit; this is a small, single-maintainer package (62 stars, 0 dependents), so pin it and plan to be able to vendor it.
  2. Nothing to publish in most setups; the service provider registers the on-demand compiler and a facade.
  3. Render your first template:
use Protonemedia\BladeOnDemand\Facades\BladeOnDemand;

$html = BladeOnDemand::render(
    'Hello, {{ $name }}! You have {{ $count }} new messages.',
    ['name' => 'Pascal', 'count' => 3]
);

Where to look first

  • src/ — the service provider (what it binds and whether it swaps the global Blade compiler) and the compiler class that overrides the compiled-path/expiry logic. That tells you whether custom Blade::directive() registrations apply.
  • The README's facade alias / FQCN — confirm the namespace on your installed version rather than copying snippets from memory.
  • Before anything else: check your Laravel version. Laravel 9+ ships Blade::render($string, $data) natively. If you're on 9 or later and you only need "string in, HTML out," you may not need this package at all. Reach for it when you need its specific extras or you're pinned to Laravel 8.

First real use case: a DB-backed email template. Store the subject/body in a templates table, let an admin edit the Blade source, and render with BladeOnDemand::render($template->body, $payload) before handing the HTML to Mail::html() or a Mailable.

Implementation Patterns

DB template + file view fallback

$html = $template
    ? BladeOnDemand::render($template->body, $data)
    : view("emails.{$key}", $data)->render();

Keep the file view as the source of truth for the default and seed the DB from it. That way a broken/empty DB row never takes down a critical flow.

Wrap it in a small service, not raw calls everywhere

Put one class between your app and the package: TemplateRenderer::render(string $key, array $data). It gives you a single place to add caching, logging, sanitization, and a fallback — essential given the package's size and bus factor.

Cache rendered output, not just compilation

Compilation caching only saves parsing. If a template is rendered for every request (e.g. a tenant banner), cache the result:

$key = 'tpl:'.md5($template->body).':'.md5(serialize($data));
$html = Cache::remember($key, now()->addHour(), fn () => BladeOnDemand::render($template->body, $data));

Invalidate on the template's updated_at, and be careful that $data contains no per-user secrets — otherwise you'll leak one user's rendering to another.

Multi-tenant / CMS overrides

Resolve the template by scope (tenant_id, locale), falling back up the chain: tenant → global → file view. This is the pattern the package is best suited for; it keeps theming data out of the filesystem and out of deploys.

Email, PDF, and notification bodies

  • Mail: render to a string, then Mail::html($html, $callback) or a Mailable that sets $this->html.
  • PDF: render, then pass the HTML to DomPDF/Snappy — a good fit since those libraries want a string anyway.
  • Notifications: render per-channel bodies (mail vs. SMS plaintext) from the same stored template with different data.

Admin preview endpoint

Expose a "preview" route that renders the draft template inside a try/catch and returns the exception message to the editor UI. This is the single highest-value integration: it turns a opaque 500 in production into a red box next to the textarea.

try {
    return response(BladeOnDemand::render($request->body, $sampleData));
} catch (\Throwable $e) {
    return response()->json(['error' => $e->getMessage()], 422);
}

Testing

Assert on the rendered string directly — no HTTP, no view file:

$this->assertStringContainsString('Hello, Pascal!', BladeOnDemand::render($src, ['name' => 'Pascal']));

Snapshot-test the default templates so an accidental edit is visible in the diff.

Gotchas and Tips

Arbitrary code execution — treat stored templates as PHP, not data. {{ }}, {!! !!}, @php, @if, and every registered directive are available. If anyone other than a trusted developer can edit a template, you have a remote-code-execution vector. Mitigate with roles/permissions on the editor, an audit log of template versions, and — if you must let non-devs edit — restrict to a whitelisted subset of directives rather than accepting arbitrary Blade. Never render a template string that arrived in a request body.

$this and view context differ from a normal view. Code that relies on the view's internal context ($this->, @include of a view that expects shared data, component class resolution) can behave differently than under view(). Pass everything you need explicitly in the data array; don't assume view()->share() or the current route's variables are present.

Custom directives: verify they work. If the package swaps in its own compiler instance, directives registered on Laravel's default compiler may not apply. Test one directive (Blade::directive('money', ...)) before building features on top. If it fails, either register on the on-demand compiler or re-register in a service provider after the package boots.

Error messages point at compiled files, not your template. Compile exceptions reference a hashed compiled path and a line number in generated PHP. Map it back by dumping the compiled source, or by isolating the failing snippet in php artisan tinker and bisecting. A @php block or malformed directive is usually the culprit.

Laravel 9+ overlap. Blade::render() does the same core job natively and is maintained by the framework. If you're on 9+, prefer it and use this package only for a specific feature you can't replicate. Document why it's a dependency, or a future upgrade will silently leave dead weight.

No cross-request compile cache guarantee. If compilation happens per call and the compiled output is in-memory only, you pay the parse cost on every render. Benchmark before putting it on a hot path; add the render cache described above.

Escaping surprises. {{ $x }} escapes (good for user data), {!! $x !!} does not. Because admins control the template, it's easy for them to introduce an XSS by using {!! !!} around a user-supplied field. Lint or review templates that contain {!!.

@extends / @section from a string is awkward. Layout inheritance expects a view file to extend. For stored templates, prefer self-contained markup or @include of normal file-based partials for shared header/footer chrome.

Keep the package replaceable. It's ~one class of logic over BladeCompiler. Write your TemplateRenderer interface so that if the package goes unmaintained or you move to Blade::render(), you change one file. Check its release date and test suite before relying on it for a revenue-path feature, and pin the version in composer.json.

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.
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox