Weave Code
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 Turbo Laravel Package

symfony/ux-turbo

Symfony UX Turbo integrates Hotwire Turbo into Symfony apps, enabling faster navigation, Turbo Frames/Streams updates, and smoother UX with minimal custom JavaScript. Includes Stimulus integration and tools to progressively enhance pages and forms.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the Bundle:

    composer require symfony/ux-turbo
    

    Ensure StimulusBundle is installed (dependency) and assets/ is configured in package.json.

  2. Enable in config/bundles.php:

    return [
        // ...
        Symfony\UX\Turbo\TurboBundle::class => ['all' => true],
    ];
    
  3. Add Turbo to Your Layout: Include the Turbo JS in your base template (e.g., base.html.twig):

    {{ encore_entry_script_tags('app') }} {# Assumes Turbo is included via StimulusBundle #}
    

    Or manually:

    <script src="https://unpkg.com/@hotwired/turbo@latest"></script>
    
  4. First Use Case: Turbo Frame Create a frame in a template:

    <turbo-frame id="dynamic_content">
        {% include 'partials/content.html.twig' %}
    </turbo-frame>
    

    Target it from a controller:

    use Symfony\UX\Turbo\TurboBundle;
    
    return $this->render('partials/content.html.twig', [
        // Data for the frame
    ])->setTurboFrame('dynamic_content');
    

Where to Look First

  • Official Documentation: Start with the "Chat Example" for a full workflow.
  • Twig Components: Explore <twig:Turbo:Frame>, <twig:Turbo:Stream>, and <turbo-mercure-stream-source> for common patterns.
  • Controller Helpers: Use TurboBundle::STREAM_FORMAT for content negotiation and $this->render()->setTurboFrame() for targeted updates.

Quick Win: Turbo Links

Replace standard links with Turbo-enhanced ones:

<a href="{{ path('app_home') }}" data-turbo="true">Home</a>

This enables instant navigation without full page reloads.


Implementation Patterns

1. Turbo Frames for Isolated Updates

Pattern: Use frames to update specific parts of the page without reloading the entire DOM. Workflow:

  1. Define a Frame in Twig:
    <turbo-frame id="notifications">
        {% include 'components/notifications.html.twig' %}
    </turbo-frame>
    
  2. Target the Frame from a Controller:
    return $this->render('components/notifications.html.twig', [
        'notifications' => $newNotifications,
    ])->setTurboFrame('notifications');
    
  3. Trigger Updates:
    • Via AJAX calls (e.g., polling or WebSocket events).
    • From Mercure streams (see below).

Tip: Use turbo_frame_request_id() in Twig to customize responses for frame requests:

{% if turbo_is_frame_request() %}
    <div data-turbo-frame="{{ turbo_frame_request_id() }}">...</div>
{% endif %}

2. Turbo Streams for Server-Pushed Updates

Pattern: Broadcast DOM changes to clients via Turbo Streams (e.g., real-time notifications). Workflow:

  1. Set Up Mercure (if using real-time):

    composer require symfony/mercure-bundle
    

    Configure in config/packages/mercure.yaml.

  2. Broadcast a Stream in a Controller:

    use Symfony\UX\Turbo\Attribute\Broadcast;
    
    #[Broadcast]
    public function addNotification(Notification $notification): TurboStreamResponse
    {
        return $this->render('components/notification.stream.html.twig', [
            'notification' => $notification,
        ]);
    }
    

    Twig template (notification.stream.html.twig):

    {{ turbo_stream_append('notifications') }}
        <div>{{ notification.message }}</div>
    {{ end_turbo_stream_append }}
    
  3. Listen for Streams in the Client:

    <turbo-mercure-stream-source src="{{ path('mercure') }}" topic="notifications">
        <turbo-stream-action>append</turbo-stream-action>
    </turbo-mercure-stream-source>
    

    Or via Twig:

    {{ turbo_stream_from('notifications') }}
    

3. Progressive Enhancement with Fallbacks

Pattern: Ensure functionality works with/without JavaScript. Workflow:

  1. Use data-turbo="false" for Critical Links:
    <a href="{{ path('checkout') }}" data-turbo="false">Checkout</a>
    
  2. Fallback for Turbo Frames:
    {% if not turbo_is_frame_request() %}
        <div class="fallback-content">
            {{ include('partials/content.html.twig') }}
        </div>
    {% endif %}
    

4. Integration with Symfony Forms

Pattern: Handle form submissions without full page reloads. Workflow:

  1. Add Turbo to the Form:
    {{ form_start(form, { attr: { 'data-turbo': 'true' } }) }}
        {{ form_widget(form) }}
    {{ form_end(form) }}
    
  2. Return a Turbo Stream Response:
    public function submitForm(Request $request): TurboStreamResponse
    {
        $form->handleRequest($request);
        if ($form->isSubmitted() && $form->isValid()) {
            return $this->render('components/flash.stream.html.twig', [
                'message' => 'Success!',
            ]);
        }
        return $this->render('form.html.twig', ['form' => $form]);
    }
    

5. Custom Actions and Modals

Pattern: Use Turbo’s visit action for modals or external pages. Workflow:

  1. Trigger a Modal:
    <button data-turbo-action="visit" data-turbo-frame="_modal">Open Modal</button>
    <turbo-frame id="_modal" src="{{ path('modal_page') }}"></turbo-frame>
    
  2. Handle the Modal in a Controller:
    public function modalPage(): Response
    {
        return $this->render('modal.html.twig');
    }
    

6. Asset Mapping and Stimulus

Pattern: Combine Turbo with Stimulus for enhanced interactivity. Workflow:

  1. Define a Stimulus Controller:
    // assets/controllers/hello_controller.js
    import { Controller } from '@hotwired/stimulus';
    
    export default class extends Controller {
        connect() {
            console.log('Hello from Stimulus!');
        }
    }
    
  2. Use in Twig:
    <div data-controller="hello">
        <button data-action="click->hello#greet">Click Me</button>
    </div>
    
  3. Combine with Turbo:
    <turbo-frame id="dynamic_content" data-controller="hello">
        {% include 'partials/content.html.twig' %}
    </turbo-frame>
    

Gotchas and Tips

Pitfalls

  1. Missing Accept: text/vnd.turbo-stream Header:

    • Issue: Turbo Stream responses may not render if the Accept header is incorrect.
    • Fix: Ensure controllers return TurboStreamResponse or set the format explicitly:
      $request->setRequestFormat(TurboBundle::STREAM_FORMAT);
      
  2. Frame ID Mismatches:

    • Issue: setTurboFrame() must match the id in the Twig template.
    • Fix: Use turbo_frame_request_id() in Twig to debug:
      {{ dump(turbo_frame_request_id()) }}
      
  3. Mercure Authentication:

    • Issue: Unauthorized access to Mercure topics.
    • Fix: Configure JWT or API key auth in mercure.yaml:
      mercure:
          hubs:
              default:
                  url: '%env(MERCURE_URL)%'
                  jwt: '%kernel.project_dir%/var/mercure.jwt'
      
  4. StimulusBridge Conflicts:

    • Issue: Stimulus controllers not loading.
    • Fix: Ensure assets/controllers.json is updated and StimulusBundle is configured:
      php bin/console assets:install
      npm run build
      
  5. Caching Headers:

    • Issue: Turbo Frames or Streams being cached aggressively.
    • Fix: Add Cache-Control: no-cache headers for dynamic responses:
      $response->setPublic();
      $response->setMaxAge(0);
      
  6. Doctrine Proxy Issues:

    • Issue: Broadcasted entities with lazy-loaded proxies failing.
    • Fix: Use #[Broadcast] with fetch: 'EAGER' or manually hydrate proxies:
      $entity->initializeLazyCollections();
      

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.
yandex/translate-api
voku/simple_html_dom
league/flysystem-vfs
bkwld/upchuck
filament/spatie-laravel-tags-plugin
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php