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

Viewcomponent Laravel Package

starychfojtu/viewcomponent

A Laravel package for creating reusable, class-based view components, helping keep Blade templates clean and UI logic organized. It offers a simple way to define, render, and compose components across your application.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle, not a Laravel package, so treat it like adding a third‑party service bundle to a Symfony 3.3 app: install, register the bundle in AppKernel, then use it from Twig or PHP.

composer require starychfojtu/viewcomponent

In app/AppKernel.php (Symfony 3.3 uses the old kernel layout):

$bundles = [
    // ...
    new Starychfojtu\ViewComponentBundle\ViewComponentBundle(),
];

Where to look first, in order:

  1. The bundle's ViewComponent base class — this defines the contract you subclass (constructor props and the method that renders/outputs the component).
  2. The bundle's Twig extension — it registers the function you call from templates (a component(...)-style helper). Confirm the exact function name and argument order here before writing templates; it is the single most important file. As of v0.4.0 this file also defines the new cache parameter, so read the argument handling there rather than trusting the release note's one-line description.
  3. DependencyInjection/ — to see whether there is any config you can tune (template path, tag name for auto-registration, and now any cache-related defaults).

First use case: build a small, obviously reusable leaf component such as an Alert or Button.

// src/AppBundle/ViewComponent/AlertComponent.php
namespace AppBundle\ViewComponent;

use Starychfojtu\ViewComponentBundle\ViewComponent;

class AlertComponent extends ViewComponent
{
    private $type;
    private $message;

    public function __construct(string $type = 'info', string $message = '')
    {
        $this->type = $type;
        $this->message = $message;
    }

    public function render(): string
    {
        return $this->renderTemplate('@App/view_component/alert.html.twig', [
            'type'    => $this->type,
            'message' => $this->message,
        ]);
    }
}
{# app/Resources/views/view_component/alert.html.twig #}
<div class="alert alert-{{ type }}">{{ message }}</div>

Then use it from any template (verify the helper name/signature against the bundle's extension):

{{ component('AppBundle\\ViewComponent\\AlertComponent', {'type': 'danger', 'message': 'Boom'}) }}

If that renders, you have the full loop working and can start moving repeated markup out of templates. Note there is an immediate parallelism for a Laravel developer: this is the conceptual ancestor of Blade components / anonymous Blade components, and of what later became symfony/ux-twig-component. Your mental model of "class + template + props" transfers directly.

Once the loop works, try the v0.4.0 caching flag on a deliberately static component (a footer, a logo lockup) before using it anywhere user-specific — and confirm the parameter's exact name and semantics from the Twig extension class first.

Implementation Patterns

Props as constructor arguments. The cleanest pattern with this bundle is to make the component's constructor the prop contract: scalars, arrays, and occasionally small value objects. Keep them readonly in spirit (assign to private fields, don't mutate). This gives you typed, self-documenting components instead of a pile of Twig set statements.

Wrap, don't replace, include. Use components for things with real logic or a stable public API (buttons, alerts, cards, breadcrumbs, pagination, empty-states). Keep using plain include/embed for one-off fragments; migrating every partial to a class is churn with no payoff.

Composition over configuration. Build small components and let them contain each other by rendering inside the component's own Twig template. A CardComponent template can call component('...ButtonComponent', ...). Keep the base components dumb and let page-level templates compose them.

Cache at the leaf, not the tree (v0.4.0). The new per-call Twig parameter lets you cache a whole component's rendered output. Use it where the payoff is real and the inputs are stable: icon/logo lockups, static nav shells, marketing footers, legal disclaimers, version stamps that only change on deploy. Do not cache a component that composes other components whose output varies per request — you would be caching the composed subtree, and the invalidation story gets muddy fast.

Decide the cache scope before you turn it on. Work out from the extension class whether the cache key includes the props you pass. If it does not, a cached AlertComponent will serve the first visitor's message to everyone. When in doubt, only cache components whose render output is a pure function of the class, not the arguments — that is the safe default with a one-line feature like this.

Keep data fetching out of the component. The temptation is to inject a repository into the component and let it query. Resist it: components receive already-hydrated data, so they stay renderable in isolation and trivially testable. Do the query in the controller and pass the result in. This also keeps the caching decision understandable — a cached component with a hidden query is a debugging nightmare.

Presenter-style usage against entities. For a UserBadgeComponent, pass the User entity and let the component (or its template) decide field presentation. This is the nearest thing to a Laravel view composer, but explicit and scoped per call site rather than global. Note that per-user components are exactly the ones you must not cache.

Controller integration. For pages that are entirely one component, return the component's rendered string from a controller action rather than duplicating the template lookup. This is especially handy for AJAX/partial endpoints that return a fragment for a JS request.

Testing. Because a component is a plain PHP object with a render() method, you can unit test the non-Twig logic directly, and integration-test rendering by booting the kernel and asserting the output contains expected markers. You get a much tighter test surface than asserting against a full page render. Add at least one test that renders the same cached component twice with different props and asserts the outputs differ — that single test is your regression guard against the cache-key footgun.

Migration path. If you are starting fresh, prototype with this bundle only if you are pinned to Symfony 3.3. Otherwise go straight to symfony/ux-twig-component, which is the maintained descendant of this exact idea — the class/template/props structure is nearly the same, and it has a documented, first-class cache story, so a prototype here ports over with modest edits (and you can drop the homemade caching flag entirely).

Gotchas and Tips

  • Effectively abandoned, single-commit era. Last release was September 2017; v0.4.0 is a feature addition on top of a still-0.x, zero-star, zero-dependent package. There is no issue tracker activity to lean on. Treat it as vendored code you own: pin the exact version in composer.json (now 0.4.* if you want the cache feature), commit composer.lock, and be prepared to fork if PHP or Twig upgrades break it.
  • The cache parameter is undocumented beyond one sentence. Read the Twig extension and the base class to answer four questions before relying on it: what the parameter is called, how it is passed (boolean? TTL? key?), what the cache key actually consists of, and where the entry is stored (Symfony cache pool? filesystem?). Anything you cannot answer, assume "cache key is the class name only" and scope your usage accordingly.
  • Cached output vs. Twig's own cache. Twig already compiles templates to PHP and caches them; this new flag caches the rendered string. They are different layers with different invalidation. A component template change will not necessarily bust the component render cache — plan to clear the app cache on deploy, and check whether you need to bump a cache key/version when markup changes.
  • Dev vs. prod surprises double with caching. New component templates that render fine in dev but not in prod are almost always the compiled Twig cache (bin/console cache:clear --env=prod). New markup that does not appear after deploy may now additionally be the v0.4.0 render cache — check both before you start editing Twig files.
  • Container access vs. caching. If the cache is implemented by fetching a cache service from the container inside the base class, you inherit the same Symfony 3.3 private-service problem as the Twig lookup: "service not found / private service". Declare the cache service public in services.yml, or (preferred) override the base class so both Twig and the cache pool are injected.
  • Symfony 3.3 is the target. It assumes the pre-Flex AppKernel/app/Resources layout and the Symfony 3.x DI and Twig conventions. Do not expect it to just work on Symfony 5/6 — service definitions, twig service lookup, and bundle registration style all changed. The v0.4.0 cache layer, if it touches cache pools, is the most likely thing to break on a modern Symfony.
  • Template path resolution is the #1 source of TemplateNotFound. Bundle-relative (@Bundle/...) versus app-relative (AppBundle:dir:file.html.twig) versus app/Resources/views/... all resolve differently depending on which loader you hit. Print the path you're passing into renderTemplate() and confirm the file exists at that exact key. When in doubt, use the @Bundle/... form.
  • Verify the Twig helper before committing to a syntax. The function name, argument order, and now the extra cache argument are the API details you must confirm from the bundle's Twig extension class. Don't pattern-match from a blog post — this package is obscure enough that stale examples are likely, and the newest example is a single release-note sentence.
  • Component classes need to be resolvable from Twig. If the helper takes a class string, the class must be autoloadable and its dependencies wiring-clean. Keep components in a dedicated ViewComponent/ namespace, mark them as services if you need DI, and avoid autowiring the world into leaf components — especially cached ones, where a dependency captured at write time may not be the one you want at read time.
  • Escape output correctly in the component template. Because the component owns the markup, this is where XSS sneaks in on message/title props. Rely on Twig autoescape and use |raw only for genuinely pre-sanitized content. Don't let a raw prop cross the component boundary, and be extra careful if a cached component ever renders per-user content.
  • Beware hidden state in props you pass by reference. Arrays and entities passed in are shared; if a component mutates them (sorting a collection, for instance) the caller sees it. Copy into a local if you need to transform. This is worse under caching, where a mutation can leak into a stored render.
  • Extension points are thin, by design. Realistically your extension points are: subclassing ViewComponent, adding your own tag/compiler pass on top of the bundle's registration, and wrapping the Twig function with your own (the natural place to supply a default cache policy rather than repeating a flag at every call site). Don't hunt for config options that likely don't exist; the bundle is small enough to read in an afternoon, which is the recommended onboarding step.
  • Have an exit plan documented. Because this is a 2017 zero-traction component, write a short note in your repo: which components you built, which classes extend the bundle, where you enabled the v0.4.0 cache flag, and how you'd port them to symfony/ux-twig-component or plain Twig embed. That note is what makes this safe to adopt.
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.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment