CLOUD TEMPLATES In-depth · 10 min read

Template Editor

Cloud templates are Blade documents stored in BladePDF and rendered by their template id. You author them in the dashboard's template editor — layout, settings, and assets all live with the template, so your Laravel app only sends data. This page covers how the editor works, the asset:/// scheme, and the exact Blade syntax the cloud renderer supports.

When to use cloud templates

Cloud templates are a good fit when:

  • Non-developers need to edit document layout without a deploy.
  • The same document is rendered from several apps or languages.
  • You want template changes, versioning, and assets managed in one place.

If your template already lives in your Laravel views, keep using local Blade views with fromView() instead. The two approaches share the same rendering engine.

The editor

The template editor is split into four areas:

  • Editor — the Blade source of the document.
  • Settings — page format, margins, orientation, and other render options stored with the template.
  • Assets — images, CSS, and fonts uploaded and referenced with the asset:/// scheme.
  • Preview — a live render using sample context so you can see the PDF while you edit.

Drafts, publishing, and versions

Editing a template updates a draft. Renders always use the published version, so your changes are not live until you publish. This lets you iterate safely against production traffic.

  • Save stores the current draft without affecting live renders.
  • Publish promotes the draft to the live version used by templateId.
  • Restore rolls back to an earlier published version.
  • Duplicate creates a copy to experiment with or branch from.
  • Archive / Restore retires a template without deleting it, and brings it back if needed.
  • Download exports the template source, and Star pins frequently used templates.
Rendering an unpublished template

A render targeting a template that has never been published will fail. Publish at least once before calling fromTemplate() with its id.

Template settings

A cloud template owns its own page setup. Format, margins, orientation, background, header and footer, and wait behavior are configured in the editor's Settings panel and stored with the template — not sent per request.

Header and footer overrides don't apply to cloud templates

Because layout is part of the stored template, withHeader() / withFooter() HTML overrides are ignored for fromTemplate() renders. Configure headers and footers in the editor instead. Those overrides are only for local view and raw HTML renders.

Context data

Templates receive a JSON context object — the data your document renders. From Laravel you pass it as the second argument to fromTemplate(); over the REST API it is the context.json file. Because context crosses the network as JSON, values must be JSON-serializable (arrays and scalars), so convert models with toArray() first.

Sample context
json
 1{
 2  "invoice": { "number": "INV-0001", "total": "€1,240.00" },
 3  "customer": { "name": "Acme GmbH" },
 4  "items": [
 5    { "description": "Design retainer", "amount": "€1,000.00" },
 6    { "description": "Hosting", "amount": "€240.00" }
 7  ]
 8}

Set a sample context in the editor so the live preview has realistic data to render. The sample is only used for previews — real renders use the context you send.

Assets and the asset:/// scheme

Upload images, fonts, and stylesheets in the Assets panel, then reference them from your template with the asset:/// scheme. At render time BladePDF resolves each reference to the stored asset.

Referencing assets
html
 1<img src="asset:///logo.svg" alt="Logo">
 2
 3<style>
 4  @font-face {
 5    font-family: "Brand";
 6    src: url("asset:///fonts/brand.woff2");
 7  }
 8  .hero { background-image: url("asset:///hero.png"); }
 9</style>
  • Asset names are simple file names — letters, numbers, dots, underscores, and hyphens (a name may include folder-style segments such as fonts/brand.woff2).
  • Uploaded assets count against your workspace storage quota.
  • The same asset:/// reference can be swapped for a single render from Laravel with overrideAsset() — useful for per-tenant logos.
Per-request asset override (Laravel)
php
 1return BladePDF::fromTemplate('invoice.standard', $context)
 2    ->overrideAsset('logo.svg', public_path("tenants/{$tenant->id}/logo.svg"))
 3    ->render()
 4    ->download('invoice.pdf');

Supported Blade syntax

The cloud renderer is a sandboxed, Blade-compatible engine. It supports the template-oriented parts of Blade:

Group Supported
Echoes{{ }}, {!! !!}, @{{ }}, @@directive
Comments & verbatim{{-- --}}, @verbatim … @endverbatim
Conditionals@if, @elseif, @else, @unless, @isset, @empty, @switch/@case/@default
Loops@foreach, @forelse, @for, @while, @break, @continue (with $loop)
Includes@include, @includeIf, @includeWhen, @includeUnless, @includeFirst, @each
Layouts & sections@extends, @section, @yield, @parent, @show, @hasSection, @sectionMissing
Stacks@push, @prepend, @stack, @once
Helpers@json, @js, @class, @style

Unsupported syntax

Anything that would execute arbitrary code or reach into the Laravel runtime is not available in cloud templates:

  • PHP execution@php, <?php, <?=, @inject, @use.
  • Laravel runtime helpers — auth, policies, env, Vite, Livewire, session, and routing directives.
  • Blade components & slots<x-*>, <x-slot>, @props, @aware.
  • Arbitrary method calls on objects passed in via context.

The pattern is simple: do computation in your application, not the template. Format currency, dates, and totals before you send the context, and pass the finished strings and arrays. Use @include and @extends for reuse instead of components.

Safety model

Templates run without eval, new Function, or a VM. A safe expression parser blocks dangerous keys like __proto__ and constructor, and hard limits cap output size, render time, loop iterations, include depth, and expression complexity. Keep templates focused on presentation and they will stay well within these limits.

A complete example

A layout, a document that extends it, an asset, a loop, and safe echoes:

layouts/base
blade
 1<!doctype html>
 2<html>
 3<head>
 4    <meta charset="utf-8">
 5    <style>
 6        body { font-family: sans-serif; color: #111827; padding: 40px; }
 7        h1 { color: #ff2d20; }
 8        table { width: 100%; border-collapse: collapse; margin-top: 24px; }
 9        th, td { text-align: left; padding: 8px; border-bottom: 1px solid #e5e7eb; }
10    </style>
11</head>
12<body>
13    <img src="asset:///logo.svg" height="40" alt="Logo">
14    @yield('content')
15</body>
16</html>
invoice.standard
blade
 1@extends('layouts.base')
 2
 3@section('content')
 4    <h1>Invoice {{ $invoice['number'] }}</h1>
 5    <p>Billed to: {{ $customer['name'] }}</p>
 6
 7    <table>
 8        <thead>
 9            <tr><th>Description</th><th>Amount</th></tr>
10        </thead>
11        <tbody>
12            @forelse ($items as $item)
13                <tr>
14                    <td>{{ $item['description'] }}</td>
15                    <td>{{ $item['amount'] }}</td>
16                </tr>
17            @empty
18                <tr><td colspan="2">No line items</td></tr>
19            @endforelse
20        </tbody>
21    </table>
22
23    <p><strong>Total: {{ $invoice['total'] }}</strong></p>
24@endsection

Render it from Laravel

Once published, render the template by its id and pass the context:

PHP
php
 1return BladePDF::fromTemplate('invoice.standard', [
 2    'invoice' => $invoice->toArray(),
 3    'customer' => $invoice->customer->toArray(),
 4    'items' => $invoice->items->toArray(),
 5])
 6    ->reference($invoice->uuid)
 7    ->storePdf()
 8    ->render()
 9    ->download("invoice-{$invoice->number}.pdf");

See Blade Templates for how local and cloud renders compare, and the facade reference for every builder method.

Was this page helpful?
Let us know if something's missing or unclear.