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

Ux Blocks Laravel Package

krystal-sf/ux-blocks

UX Blocks for Laravel provides a library of reusable, ready-made UI blocks for your Blade views, helping you assemble polished interfaces faster with consistent styling and minimal markup. Great for dashboards, marketing pages, and admin panels.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: krystal-sf/ux-blocks is a Symfony UX package — Twig templates, Symfony DI tags, a compiler pass. It has no Laravel service provider, no Blade views, and no Laravel-facing API. composer require krystal-sf/ux-blocks into a Laravel app will at best install dead weight and at worst drag in Symfony/Twig constraints that fight your framework version. The correct day-to-day use of this package in a Laravel team is as a reference implementation: read it, extract the tagged-block pattern, own ~200–400 lines of Blade-native code.

Minimal steps, in order:

  1. Clone and read, don't require.
    git clone https://gitlab.com/krystal-sf/ux-blocks /tmp/ux-blocks
    
    Start at composer.json (PHP/Symfony/Twig constraints — this tells you if it could even co-exist with your app), then src/.
  2. Find the tag name. Grep for the compiler pass and the interface:
    grep -rn "addTaggedService\|findTaggedServiceIds\|CompilerPass" src/
    grep -rn "interface .*Block" src/
    
    The string passed to findTaggedServiceIds() (or the autoconfigure tag declaration in the DI extension) is the whole contract. Write it down.
  3. Read the renderer. Find the service that turns a tag string into output. Look specifically at: what happens on an unknown tag, how blocks are ordered (priority? registration order?), and whether rendering is coupled to the Twig runtime or sits behind an injectable interface. That last point decides whether anything is salvageable.
  4. Read tests/. With zero docs and zero stars, tests are the only spec.

First use case (the one you should actually ship): a BlockRegistry in your Laravel app so a marketing page can be assembled from config/blocks.php instead of hardcoded Blade @includes.

// app/Blocks/BlockRegistry.php
namespace App\Blocks;

use Illuminate\Support\Facades\View;

class BlockRegistry
{
    protected array $blocks = [];

    public function register(string $tag, array $definition): static
    {
        if (isset($this->blocks[$tag]) && app()->isProduction()) {
            report(new \RuntimeException("Duplicate block tag [$tag]"));
        }
        $this->blocks[$tag] = $definition + ['view' => null, 'priority' => 0, 'strict' => null];
        return $this;
    }

    public function has(string $tag): bool { return isset($this->blocks[$tag]); }

    public function all(): array
    {
        return collect($this->blocks)->sortByDesc('priority')->all();
    }

    public function render(string $tag, array $data = []): string
    {
        $block = $this->blocks[$tag] ?? null;
        $strict = $block['strict'] ?? app()->isLocal();

        if (! $block || ! $block['view'] || ! View::exists($block['view'])) {
            $strict
                ? throw new \RuntimeException("Block [$tag] is not resolvable.")
                : logger()->warning("Block [$tag] missing; rendering nothing.");
            return '';
        }

        return view($block['view'], $data)->render();
    }
}

Bind it once, load config in the provider, and expose a Blade component:

// app/Providers/BlockServiceProvider.php
public function register(): void
{
    $this->app->singleton(BlockRegistry::class, function () {
        $registry = new BlockRegistry;
        foreach (config('blocks', []) as $tag => $definition) {
            $registry->register($tag, $definition);
        }
        return $registry;
    });
}
{{-- resources/views/components/block.blade.php --}}
@props(['tag', 'data' => []])
{!! app(\App\Blocks\BlockRegistry::class)->render($tag, $data) !!}

Then config/blocks.php maps tags to views, and a page becomes <x-block tag="hero" />. If you cannot get that single block rendering within an hour, stop — that is your assessment result.

Implementation Patterns

Pattern 1 — config-driven tag → view (start here). For marketing/campaign pages the registry is a map, not a class hierarchy.

// config/blocks.php
return [
    'hero'        => ['view' => 'blocks.hero',        'priority' => 100],
    'feature-grid'=> ['view' => 'blocks.feature-grid','priority' => 50],
    'cta'         => ['view' => 'blocks.cta',          'priority' => 10],
];

Keep block views colocated: resources/views/blocks/hero.blade.php, plus blocks/hero.css.js if you need a scoped script via @vite/@push('scripts'). This replaces the package's "self-contained block bundles template, logic, assets" claim with Vite + Blade partials.

Pattern 2 — class-based blocks (mirror the Symfony interface). Once blocks get logic, port the interface shape directly: one class per block, a tag() and a render(array $data): View|string.

interface BlockContract
{
    public function tag(): string;
    public function view(): string;
    public function data(array $context = []): array; // defaults + validation
}

Register them in the same provider ($registry->register($block->tag(), ['view' => $block->view()])). This keeps the package's extension seam — a compiler-pass-style collect-then-sort — intact, but in Laravel idioms.

Pattern 3 — composition and nesting. Blocks assemble pages, blocks nest blocks. Render order comes from priority (or an explicit weight array passed at render time when the page editor controls sequencing):

@foreach (app(\App\Blocks\BlockRegistry::class)->forPage('home') as $tag)
    <x-block :tag="$tag" />
@endforeach

Pattern 4 — cache the resolved list, not the rendered HTML. The registry is static per request/build; memoize all() in the singleton (done above by construction). Cache rendered blocks separately and explicitly, and key on the block tag + input data hash + view cache key, never the tag alone.

Integration tips.

  • Expose a single Blade component (<x-block>) rather than injecting the registry into controllers. The package's Twig function/tag is the analogue; keep the entry point declarative.
  • Dispatch BlockRendering / BlockRendered events around render() — that is the cheap extension point for analytics, AB tests, and cache warmers, and the one thing worth copying structurally from the original.
  • Keep escaping policy per block in config ('raw' => true for CMS-authored HTML), not decided at the call site.
  • If you need interactivity inside a block, use Livewire components inside the block view; do not try to make the registry async.

Gotchas and Tips

  • Don't install it in Laravel. The realistic integration requires a Twig bridge, Symfony container tag semantics, the UX component runtime, and Twig namespace registration — four moving parts for something Blade does natively. Record this decision once in an ADR so it does not get re-litigated every quarter.
  • Unknown tag is the highest-probability production bug. The ported version must render an empty string and log, with a strict flag that throws only in local/CI. Silent 500s on a marketing page after a content deploy is the classic failure. Never inherit a throw default from a Symfony dev environment.
  • Missing view = placeholder, not exception. A registered tag whose view was renamed should degrade to an HTML comment <!-- block:hero missing --> so the page still ships. Assert both branches in tests.
  • Duplicate registration: fail at boot, not at render. Two blocks registered under hero produces unmaintainable content bugs because last-wins is invisible. Throw in non-production, log loudly in production, and document the precedence.
  • Cache invalidation is the post-deploy incident you will have. Block views must be part of php artisan view:cache; run view:clear (or a view:cache) in your deploy script. If you cache rendered blocks in Redis, include a build/version stamp in the key or you will serve last release's hero indefinitely.
  • Escaping. Any block that accepts editor/CMS HTML is an XSS surface. Keep {!! !!} confined to the render() implementation, sanitize with mews/purifier or HTMLPurifier, and gate it behind an explicit per-tag flag. This is the highest-severity failure mode in the whole pattern.
  • Ordering quirks. Whether the package sorts by tag priority or by service-definition order is exactly the kind of undocumented behaviour you must verify in src/ before copying it. In your port, sort explicitly (sortByDesc('priority')) and make ties deterministic (tag name as secondary key).
  • Version and ownership risk. Zero stars, zero dependents, no changelog, single-organisation maintainership. If you do read the source, pin the commit you read in a comment and treat the package as frozen. Assume you become the maintainer of anything you copy.
  • Extension points worth preserving in your port: the registry (register()/extend()), the contract interface, the render events, and the config/blocks.php map. Everything else — Twig tags, DI autoconfiguration, the bundle class — is Symfony machinery you are deliberately not porting.
  • Debugging tip: when a block renders blank, check in this order — tag present in config, view path correct relative to resources/views, view:cache stale, escaping swallowing output, then exception being swallowed by a catch-all in your renderer. Log the resolved view path on every render at debug level.
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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
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