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

Sirtrevorjs Laravel Package

caouecs/sirtrevorjs

Laravel integration for the Sir Trevor block-based content editor. Ships a service provider, config, and Blade helpers for storing and rendering Sir Trevor JSON blocks, with customizable block parsers and image handling.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and let Laravel discover it (auto-discovery works on 5.5+; on 4.x/5.x add Caouecs\Sirtrevorjs\SirtrevorjsServiceProvider and the SirTrevorJs facade alias to config/app.php by hand):

composer require caouecs/sirtrevorjs

If you're on Laravel 10+, stop here — the package only advertises support through Laravel 9. Test composer require in a throwaway branch first, because constraint resolution is the most likely place you'll get blocked.

2. Publish the config. This is the file you'll actually live in — it maps Sir Trevor block type strings (the exact type values the JS editor emits, e.g. Text, Heading, Image, Video) to the PHP converters / Blade views that render them:

php artisan vendor:publish --provider="Caouecs\Sirtrevorjs\SirtrevorjsServiceProvider"

3. Bring your own Sir Trevor assets. The package does not ship the editor JS/CSS. Pull sir-trevor from npm or a CDN, load jQuery first (Sir Trevor depends on it), and initialize the editor against a textarea inside your form:

<textarea name="body" class="sir-trevor-input">{{ old('body', $post->body) }}</textarea>

<script>
new SirTrevor.Editor({
  el: $('.sir-trevor-input'),
  blockTypes: ['Text', 'Heading', 'Image', 'Video', 'Quote', 'List'],
});
</script>

Sir Trevor serializes the document into that textarea as JSON ({"data":[{"type":"Text","data":{"text":"..."}}]}). Persist it verbatim into a text or json column — don't reformat it on the way in, or the editor will choke when it reloads.

4. Render it back anywhere in Blade:

{!! SirTrevorJs::render($post->body) !!}

Where to look first: the published config/sirtrevorjs.php (it's the definitive list of supported block types) and the package's resources/views partials, which you'll publish and override. First use case: a blog post or CMS page body field — store the editor JSON, render HTML on the public page.

Implementation Patterns

Keep rendering out of your views. Expose a method on the model so templates stay dumb and the render call is testable in isolation:

class Post extends Model
{
    protected $casts = ['body' => 'array'];

    public function renderedBody(): string
    {
        return $this->body
            ? SirTrevorJs::render($this->getRawOriginal('body'))
            : '';
    }
}

Cache the render, bust it from an observer. Every block dispatches to a converter and renders a Blade partial, so a 20-post listing is a lot of view work. Render once, store or cache the HTML, and invalidate on save:

protected static function booted()
{
    static::saved(fn ($post) => Cache::forget("post.{$post->id}.body"));
}

The stronger version is a body_html column written on save — it also freezes the exact markup readers saw, so changing a block template later doesn't retroactively mutate published pages.

Override block markup without forking. Publish the views and edit the partial for the types you care about, or repoint the config entry at your own converter class (subclass the shipped one, override render, register it under the same type key). That config map is the extension point — treat it as your block registry.

Custom blocks need both halves to agree on the string. JS side: SirTrevor.Blocks.YourBlock = SirTrevor.Block.extend({...}) and add 'YourBlock' to blockTypes. PHP side: a converter + view registered under the identical 'YourBlock' key. A one-character mismatch renders an empty div with no error.

Wire uploads yourself. Sir Trevor has no backend; hook its upload event to a controller that stores the file and returns the shape it expects:

public function upload(Request $request)
{
    $path = $request->file('attachment')->store('uploads', 'public');
    return response()->json(['file' => ['url' => Storage::url($path)]]);
}

Denormalize a plain-text rendition. Sir Trevor JSON is miserable to search. On save, walk the decoded blocks and concatenate the text fields into body_plain for LIKE, Scout, or TNTSearch — same column gives you excerpts and meta descriptions for free.

Use the light render mode in listings. The package distinguishes full rendering from lighter/API-backed modes for oEmbed-style blocks (tweets, videos). Use the light variant on index/card views so you aren't stamping heavy iframes into every teaser.

Sanitize inside the pipeline, not in the template. Wrap the render once:

$html = clean(SirTrevorJs::render($post->body), 'post'); // mews/purifier or similar

with an allowlist covering what your templates actually emit (figure, iframe, blockquote, ...). Editor content is untrusted HTML by definition.

Draft vs published. Render eagerly into body_html on publish; render on the fly for previews so template edits show up immediately without touching frozen published content.

Gotchas and Tips

Laravel version is the hard ceiling. Support stops at Laravel 9. On 10/11/12, plan to fork or wire the assets app-side and isolate the whole thing behind a small adapter class, so the eventual replacement is a one-file swap.

Mix vs Vite will bite you. The package's asset assumptions predate Vite. If you're on a Vite app, don't fight the published asset stubs — install Sir Trevor via npm yourself and only use the package for the PHP-side rendering. The render half is where the value is anyway.

jQuery ordering. Load jQuery before sir-trevor.js, and initialize the editor after the DOM is ready. A silent no-op editor almost always means a missing or duplicated jQuery.

{!! !!} is your XSS surface. The facade returns raw HTML. Any block type you add becomes an injection vector unless the allowlist is updated deliberately. This is the single thing to get right before shipping.

Don't round-trip the JSON. If you decode, reformat, or re-encode body before saving, the editor may fail to rehydrate blocks or lose unknown types it doesn't recognize. Store the raw string; decode only for reading (search text extraction, migrations).

Unknown block types render nothing — silently. When a type in the JSON has no entry in the config map, you get a gap, not an exception. Add a fallback converter (or an assertion in tests) so dropped blocks are visible during development.

Null/empty content throws off render(). Guard for null and the empty document ({"data":[]}) at the accessor level; otherwise you'll sprinkle null checks through every view.

Large documents get expensive. A single post with 60 blocks means 60 converter dispatches plus 60 view renders. Cache aggressively, and consider rendering to body_html on save rather than per request once documents grow.

Search and migrations both hate the JSON. Plan the plain-text column and a canonical block schema now. If you ever migrate off Sir Trevor, you'll want importer/exporter tooling written against that schema, not against ad-hoc array access.

Pin and vendor. 54 stars and zero dependents means no support net. Commit or vendor the Sir Trevor JS/CSS you rely on, pin the Composer constraint, and name an owner for the fork. Treat the maintenance cost as known technical debt rather than discovering it six months in.

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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route