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

Linen Laravel Package

glhd/linen

Linen is a lightweight flat-file CMS for Laravel that turns Markdown and Blade files into a fast, database-free website. Write content in plain files, organize pages with simple routing, and publish docs or marketing pages with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Linen is a lightweight, Blade-inspired template engine for Laravel that renders to a string instead of returning an HTTP response. Its reason to exist is safety: templates are compiled by Linen's own parser into a restricted expression set rather than into raw PHP, which makes it viable for templates you didn't write yourself (CMS blocks, email/notification copy, tenant-editable content, AI-generated snippets).

Minimal path:

composer require glhd/linen
use Glhd\Linen\Linen;

$html = Linen::render('welcome', ['name' => 'Chris']);
  1. Read the README's syntax table first — it is a whitelist, not "Blade minus some things." Confirm exactly which directives (@if, @foreach, @include, etc.), echo forms ({{ }} vs {!! !!}), and helper functions are supported before you promise any syntax to template authors.
  2. Publish/inspect the config (php artisan vendor:publish --tag=linen-config or equivalent) to see the template path, file extension, and compile-cache settings — that tells you where authors put files and what to clear when things look stale.
  3. First use case to ship: a single "user-authored content block" rendered through Linen and echoed into an existing Blade shell. That exercises compile, escape, and cache in one small surface.
  4. Write two tests immediately: one asserting {{ $x }} escapes HTML, and one asserting {!! $x !!} does not. Those two tests encode the security contract you're relying on.

Implementation Patterns

  • Wrap it once. Put rendering behind a TemplateRenderer/LinenContent service or value object rather than calling the facade from controllers. You get one place for sanitization, caching, error handling, and an escape hatch if you ever swap engines.
  • Templates in the database. The common real-world pattern is templates table (key, body, updated_at) plus Linen::render($template->body, $data). Hash the body into the cache key so an edit invalidates cached output automatically.
  • Data shaping. Pass arrays or DTOs only — never Eloquent models. Models invite lazy-load N+1s during rendering and leak attributes ($user->password) to anyone who can write a template. Build an explicit view-model array in your service.
  • Blade for the shell, Linen for the content. Keep the email layout/HTML wrapper in Blade and render the user-authored middle through Linen, then {!! $safeHtml !!} after your sanitizer pass. This keeps the trusted surface in Blade.
  • Custom directives as components. Register project directives (e.g. @price($amount), @button('label')) so authors compose UI without expression access. Keep them pure: no DB calls or side effects inside a directive, or rendering becomes unpredictable and untestable.
  • Result caching. Compilation is the expensive part; rendering is cheap. For high-traffic output, cache the rendered string keyed by hash(template) + hash(serialized data + locale) with a short TTL.
  • Authoring feedback loop. Reuse the exact same renderer in a preview endpoint so what the author sees is what production emits. Catch compile exceptions there and return them as friendly, positioned error messages.
  • Testing. Golden-file tests for the handful of templates that matter, plus a table-driven test asserting hostile input ({{ system('id') }}-style attempts, unknown directives) fails closed rather than executing.

Gotchas and Tips

  • The 0.1.0 change is a framework constraint, not a feature. "Adds Laravel 13 support" almost certainly means a bumped illuminate/* requirement. Check composer.json before upgrading: if the constraint moved to ^13 only, apps on Laravel 11/12 must stay pinned to 0.0.x. On Laravel 13, run the full suite — the compiler/parser transitive dependencies may resolve to different versions than you tested against.
  • Pre-1.0 semantics. Minor bumps in 0.x are allowed to break. Pin with ~0.1.0, read every changelog, and don't assume an upgrade guide exists yet. There is no deprecation policy to lean on.
  • {!! !!} is the entire attack surface. Once authors have raw echo, escaping-by-default buys you nothing. Consider forbidding it in author-supplied templates (grep/lint on save, or a directive allowlist enforced before compile) unless you also run an HTML sanitizer.
  • Stale compiled templates. If Linen writes compiled output to disk/cache, an edited template can render the old version. Confirm whether the cache key includes content hash or mtime; add a deploy step that clears it, and remember php artisan view:clear only helps if Linen hooks Laravel's view compiler.
  • Compile errors are user-facing. Syntax errors throw during compile, and template authors are users — catch \Throwable at the service boundary and convert to a validation-style error. Never let a stack trace with absolute paths reach an authoring UI.
  • Directives are a closed set. Don't assume Blade directives exist (@php, @props, @slot, @auth and friends are probably absent or intentionally excluded). When a template does something surprising, the first debugging step is "is this actually a supported directive?" — Linen tends to fail quietly or literally rather than throwing on unrecognized syntax.
  • Config portability. Default template paths assume a conventional Laravel skeleton; non-standard layouts (packages, Inertia/Vite front-ends, multi-domain tenancy) often need the config published just to point at the right directory. Publish it on day one rather than relying on defaults.
  • Extension points are the whitelist. Custom directives plus whatever function/helper registry is exposed are the only sanctioned seams. Avoid monkey-patching the compiler — it's the least stable part of a 0.x package and will break on the next release.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource