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 Twig Component Laravel Package

symfony/ux-twig-component

Twig Components let you bind a PHP object to a Twig template, turning small reusable units like alerts, modals, or sidebars into clean, self-contained components you can render anywhere in your Symfony app.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:

    composer require symfony/ux-twig-component
    

    Ensure your composer.json meets the requirements: Symfony 7.4+ and PHP 8.4+.

  2. Enable the Twig bundle in config/bundles.php:

    return [
        // ...
        Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true],
    ];
    
  3. Create your first component:

    // src/Component/AlertComponent.php
    use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
    
    #[AsTwigComponent('alert')]
    class AlertComponent {
        public string $message = 'Default message';
        public string $type = 'info';
    }
    
  4. Create the template at templates/components/alert.html.twig:

    {# templates/components/alert.html.twig #}
    <div class="alert alert-{{ type }}">
        {{ message }}
    </div>
    
  5. Use the component in a template:

    {% component('alert', {
        message: 'Hello, Twig Components!',
        type: 'success'
    }) %}
    

    Or with the HTML syntax:

    <twig:alert message="Hello, Twig Components!" type="success" />
    

First Use Case: Reusable Alert Component

Replace hardcoded alerts in your app with a reusable AlertComponent. Define variants (e.g., success, error) via the $type prop and dynamically pass messages. This reduces template duplication and centralizes styling/logic.


Implementation Patterns

Component Registration

  • Automatic Discovery: Components are auto-discovered via the #[AsTwigComponent] attribute. No manual service registration is needed (Symfony 5.3+ autoconfiguration).
  • Custom Naming: Override the default component name (class name) with the name argument:
    #[AsTwigComponent(name: 'custom_alert')]
    class AlertComponent { ... }
    

Props and State

  • Public Properties as Props: Expose public properties directly in templates (e.g., {{ message }} instead of {{ this.message }}).

  • Computed Properties: Use the #[ExposeInTemplate] attribute to expose private methods/properties:

    #[ExposeInTemplate]
    public function getFormattedMessage(): string {
        return strtoupper($this->message);
    }
    

    Access in Twig: {{ formattedMessage }}.

  • Default Values: Set defaults in the component class:

    public string $message = 'No message provided';
    

Template Resolution

  • Default Location: Templates are resolved from templates/components/{ComponentName}.html.twig.

  • Custom Paths: Override the template path via the template argument in #[AsTwigComponent]:

    #[AsTwigComponent(template: 'path/to/custom_alert.html.twig')]
    
  • Anonymous Components: Use for one-off components without a class:

    {% component('alert', { message: 'Dynamic alert' }) %}
    

    Or with HTML syntax:

    <twig:alert message="Dynamic alert" />
    

Attributes and HTML

  • Dynamic Attributes: Use the attributes variable in templates to render dynamic HTML attributes:
    <div {{ attributes.addClass('custom-class') }}>
        {{ message }}
    </div>
    
  • Stimulus Integration: Add Stimulus controllers via attributes:
    <twig:alert attributes.defaults(stimulus_controller('alert-controller')) />
    

Lifecycle Hooks

  • Pre/Post Mount: Modify component data before/after it’s mounted:
    use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
    use Symfony\UX\TwigComponent\Attribute\Hook;
    
    #[AsTwigComponent]
    class AlertComponent {
        #[Hook('preMount')]
        public function preMount(): void {
            $this->message = 'Modified at preMount!';
        }
    }
    
  • Pre/Post Render: Intercept template rendering:
    #[Hook('preRender')]
    public function preRender(): void {
        $this->template = 'path/to/override.html.twig';
    }
    

State Management with provide/inject

  • Avoid Prop Drilling: Use provide in a parent component to share state with deeply nested children:
    {% component('parent-component') %}
    
    #[AsTwigComponent]
    class ParentComponent {
        public function provide(): array {
            return ['theme' => 'dark'];
        }
    }
    
    Inject in a child:
    #[AsTwigComponent]
    class ChildComponent {
        #[ExposeInTemplate]
        public function getTheme(): string {
            return $this->inject('theme');
        }
    }
    

Testing Components

  • Unit Testing: Use the RenderedComponent class to test rendered output:
    use Symfony\UX\TwigComponent\Test\RenderedComponent;
    
    public function testAlertComponent(): void {
        $component = new AlertComponent();
        $component->message = 'Test message';
        $rendered = RenderedComponent::createFromComponent($component);
        $this->assertStringContainsString('Test message', $rendered->getHtml());
    }
    
  • Debug Command: Inspect registered components:
    php bin/console debug:twig-component
    

Gotchas and Tips

Common Pitfalls

  1. PHP 8.4+ Requirement: Forgetting to upgrade PHP/PHPUnit (e.g., phpunit/dist.xml) will cause failures. Use:

    composer require --dev phpunit/phpunit:^11.0
    

    And update phpunit.xml.dist to use PHP 8.4+.

  2. Template Path Resolution: If templates aren’t found, verify:

    • The template exists at templates/components/{ComponentName}.html.twig.
    • The template argument in #[AsTwigComponent] is correct.
    • No typos in the component name (case-sensitive).
  3. Null Attributes: Passing null to ComponentAttributes throws an exception. Use remove() instead:

    {{ attributes.remove('data-old-attr') }}
    
  4. Stimulus Controllers: If Stimulus features (e.g., stimulus_controller) don’t work, ensure:

    • @symfony/stimulus-bridge is installed.
    • The Stimulus controller is registered in assets/controllers.json.
  5. Debug Mode Overhead: Component data is collected in the profiler by default in debug mode. Disable with:

    # config/packages/dev/twig_component.yaml
    twig_component:
        profiler:
            collect_components: false
    

Debugging Tips

  • Debug Command: List all registered components and their metadata:

    php bin/console debug:twig-component
    

    Search for a specific component:

    php bin/console debug:twig-component search alert
    
  • Profiler Integration: Use the Symfony profiler to inspect component rendering:

    • Navigate to the "Twig" tab in the profiler.
    • Click on "Components" to see rendered components, their props, and execution time.
  • Template Fallback: If a template is missing, Twig falls back to index.html.twig in the same directory. Use this for shared layouts.

Performance Considerations

  • Caching: Component templates are cached like regular Twig templates. Clear the cache after changes:
    php bin/console cache:clear
    
  • Avoid Heavy Logic in Hooks: Prefer lightweight operations in preMount/postMount hooks. Move complex logic to services injected via constructor.

Extension Points

  1. Custom Component Factories: Implement ComponentFactoryInterface to create components dynamically:

    use Symfony\UX\TwigComponent\ComponentFactoryInterface;
    
    class CustomComponentFactory implements ComponentFactoryInterface {
        public function create(string $name, array $props): object {
            return new CustomComponent($props);
        }
    }
    

    Register as a service with the twig.component.factory tag.

  2. Template Finders: Extend ComponentTemplateFinder to support custom template locations (e.g., database-driven templates).

  3. Attribute Escaping: Customize HTML attribute escaping by implementing HtmlAttributeEscaperInterface and passing it to ComponentAttributes.

Migration Notes

  • From ComponentInterface to #[AsTwigComponent]: Update legacy components:
    // Before (Symfony UX 2.x)
    class AlertComponent implements ComponentInterface {
        public static function getComponentName(): string { return 'alert'; }
    }
    
    // After (Symfony UX 3.x)
    #[AsTwigComponent('alert')]
    class AlertComponent { ... }
    
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.
unabomber/timezone
delatbabel/contacts
astritzeqiri/g-recaptcha
kevinongko/laravel-genode
developeruz/laravel-analytics
robbiep/afterthedeadline
frankkessler/incontact-laravel-oauth2-rest
jlxh/laravel-userlog
davidmgilo/timetables
qylinfly/role-permission
reflect-digital/conduit
takashato/laravel-vietqr
aitor24/laralang
kraenzle-ritter/entity-relations
dindigital/din-image
pomirleanu/image-colors
sawmainek/laravel-api-generator
delatbabel/jsonresponses
mouhamedfd/permissions-generator
eduplus/qr