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

Component Laravel Package

phug/component

phug/component adds a reusable component system to Phug, the Pug template engine for PHP. Build encapsulated UI pieces with Pug templates, attributes, and slots, then compose them cleanly in PHP views for maintainable, expressive front ends.

View on GitHub
Deep Wiki
Context7

Getting Started

phug/component is the add-on for Phug (the Pug template engine for PHP) that lets you define custom elements — HTML-like tags such as <app-alert> or <ui:button> — inside .pug templates, resolved either to a PHP class or to another Pug template. It sits on top of the core phug/phug renderer, so you always have the base engine installed alongside it.

Minimal setup:

composer require phug/component

Then register the extension on your renderer, either directly or via the options array when using Phug\Renderer:

use Phug\Renderer;
use Phug\Component\ComponentExtension;

$renderer = new Renderer([
    'paths' => [__DIR__ . '/views'],
]);

$renderer->addExtension(new ComponentExtension());

Where to look first:

  1. Phug\Component\ComponentExtension — the entry point; this is what you register and where component namespace → class/path mappings are declared.
  2. Phug\Component\Component (or the equivalent base class/interface) — the contract a PHP component must satisfy. Every class you build for a component will extend or implement this.
  3. The Phug docs on the core renderer — component tags are still compiled by the normal lexer/parser/compiler, so understanding how Phug resolves tag names and attributes is a prerequisite.

Your first real use case is almost always a presentational wrapper that would otherwise be repeated as a +mixin call with lots of arguments: a button, an alert, a card, a form field. Rewriting <app-alert type="danger">Oops</app-alert> instead of +alert('danger', 'Oops') reads better in templates and is easier for front-end developers to scan.

//- views/page.pug
doctype html
html
  body
    app-alert(type="danger")
      | Something went wrong

The extension's job is to turn that unknown tag into a call to whichever component is registered for app-alert.

Implementation Patterns

Class-based components (the primary pattern). A component is a small PHP class whose only responsibility is taking attributes + inner content and returning a rendered string (or a Pug fragment). Keep them dumb and stateless:

namespace App\Pug\Components;

use Phug\Component\Component;

class Alert extends Component
{
    public function render(): string
    {
        $type    = $this->getAttribute('type', 'info');
        $content = $this->getContents(); // inner slot

        return "<div class=\"alert alert-{$type}\">{$content}</div>";
    }
}

Register a namespace prefix so templates can refer to components without the FQCN:

$extension = new ComponentExtension([
    'namespaces' => [
        'app' => App\Pug\Components::class, // <app-alert>, <app-card>, ...
    ],
]);
$renderer->addExtension($extension);

Template-as-component pattern. For pure markup wrappers, skip PHP entirely and point the component at a .pug file. This is the analogue of Blade's anonymous components: attributes become template locals, and the children become the block.

//- views/components/card.pug
.card(class=attributes.class)
  .card-header= attributes.title
  .card-body
    block
app-card(title="Invoice #42")
  p= $invoice->total

Use this for anything that is purely structural; reach for a PHP class the moment you need formatting logic, escaping rules, or service access.

Composition and slots. Components nest naturally. Pass rendered markup into a named slot via an attribute, and default content via the block:

app-modal(title="Confirm")
  app-button(variant="primary") Yes
  app-button(variant="ghost") Cancel

Prefer named slots for anything a component may place in multiple regions of its layout (header/body/footer) rather than concatenating strings in PHP.

Integration with a service container. When a component genuinely needs a collaborator (a URL generator, a formatter, a translator), do not new it inside the template. Resolve through Phug's dependency injection (phug/dependency-injection) or, in Laravel, through the container. A common pattern is a thin factory that the extension uses to instantiate component classes with the container, keeping the component class itself injectable and unit-testable.

Laravel. phug/laravel wires a shared Renderer into the container. Register the ComponentExtension inside a service provider against that renderer instance — never create a second Renderer, or you will end up with two caches and two extension stacks, and templates will render with different behaviour depending on which code path you hit.

Workflow. Treat components like views: keep them in resources/pug/components, name the class and the tag consistently (Alert.php ↔ app-alert), and keep the attribute schema documented at the top of the class. A small getAttributes() accessor listing expected attributes turns an implicit contract into an explicit one and makes onboarding trivial.

Gotchas and Tips

Tag casing is normalized. Phug lowercases element names during parsing, so <App-Alert> and <app-alert> can behave differently from what your PSR-4 class name suggests. Register the lowercase prefix and rely on the extension's StudlyCase conversion; don't try to be clever with capital letters in templates.

Attribute naming. Dashed attributes (data-id, form-action) do not map cleanly to PHP camelCase accessors out of the box. Read them literally with getAttribute('form-action'), or add a normalizer in your base component. Mixing conventions is the single most common source of "it renders but the value is empty".

Self-closing vs. block form. <app-spinner/> and <app-spinner></app-spinner> are not always treated identically by the parser, and an unclosed block component will swallow the rest of your template. If a component takes no content, always close it explicitly or use the self-closing form consistently.

Unknown tags fail silently as HTML. If a component is not resolved, Phug happily emits the tag as literal HTML rather than throwing. If you see <app-alert ...> in the browser's DOM, the problem is registration, not rendering — check the namespace prefix, the class path, and that the extension is actually on this renderer instance.

Clear the compiled cache after changing components. Phug caches compiled templates aggressively. Editing a component's .pug file or remapping a namespace frequently requires clearing cache_dir; otherwise you will spend ten minutes debugging a change that the engine never read.

Debugging goes through the compiled PHP. Errors point at generated PHP in the cache directory, not at your .pug. Reproduce with the cache disabled, or use phug/debug to map line numbers back to the source template. When a component class throws, the stack trace usually surfaces the compiled render call — read one level up to find the offending tag.

Extension order matters. Register ComponentExtension after any extension that rewrites element nodes; otherwise your components arrive at the component resolver already transformed and may not match the expected tag shape.

Escape output deliberately. Components returning raw HTML strings bypass Pug's escaping. If any attribute or slot comes from user input, escape it inside the component (htmlspecialchars) — Pug will not do it for you because it thinks the component knows what it is doing.

Keep components presentation-only. The temptation is to let components hit the database or read the session. Resist it. A component that renders from a small, explicit array of attributes is trivial to unit-test with a fake attribute bag; one that reaches into $_SESSION is not. Pass data in from the controller or the parent template.

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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core