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 Package:

    composer require symfony/ux-turbo
    

    Ensure symfony/stimulus-bundle is installed (required for Turbo’s JavaScript integration).

  2. Enable the Bundle: Add to config/bundles.php:

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

    <script src="%turbo_entrypoint%"></script>
    

    Or use StimulusBundle’s entrypoint:

    {{ stimulus_use('turbo') }}
    
  4. First Use Case: Turbo Frame Create a frame in your template:

    <turbo-frame id="dynamic_content">
        {# Content loaded via Turbo #}
    </turbo-frame>
    

    Load content via a controller:

    use Symfony\UX\Turbo\TurboBundle;
    
    public function showDynamicContent(Request $request): Response
    {
        if (TurboBundle::STREAM_FORMAT === $request->getPreferredFormat()) {
            return $this->render('dynamic_content.stream.html.twig');
        }
        return $this->render('dynamic_content.html.twig');
    }
    
  5. Verify Turbo is Working:

    • Open Chrome DevTools (Network tab) and check for Accept: text/vnd.turbo-stream.html headers.
    • Test frame updates by triggering the controller action (e.g., via a link with data-turbo-frame="dynamic_content").

Implementation Patterns

1. Turbo Frames for Modular Updates

Pattern: Use Turbo Frames to isolate dynamic components (e.g., modals, partials). Workflow:

  • Template:
    <turbo-frame id="user_avatar_{% user.id %}">
        {{ include('user/_avatar.html.twig', { user: user }) }}
    </turbo-frame>
    
  • Controller:
    public function updateAvatar(User $user, Request $request): Response
    {
        // Update logic...
        return $this->render('user/_avatar.html.twig', ['user' => $user]);
    }
    
  • Trigger:
    <a href="{{ path('app_update_avatar', { id: user.id }) }}"
       data-turbo-frame="user_avatar_{% user.id %}">
        Update Avatar
    </a>
    

Tip: Use turbo_frame_request_id() in Twig to detect frame requests:

{% if turbo_is_frame_request() %}
    {# Frame-specific logic #}
{% endif %}

2. Turbo Streams for Real-Time Updates

Pattern: Broadcast DOM changes via Turbo Streams (with or without Mercure). Workflow:

  • Controller with @Broadcast Attribute:
    use Symfony\UX\Turbo\Attribute\Broadcast;
    
    #[Broadcast]
    public function notifyUser(User $user, string $message): Response
    {
        return $this->render('notification/_stream.html.twig', [
            'message' => $message,
        ]);
    }
    
  • Template:
    <div id="notifications">
        {# Turbo will inject streams here #}
    </div>
    
    <turbo-stream>
        <template id="notification_template">
            <div>{{ message }}</div>
        </template>
    </turbo-stream>
    

Mercure Integration:

<turbo-mercure-stream-source
    src="{{ path('mercure_hub') }}"
    topics="{{ ['https://example.com/.well-known/mercure'] }}">
</turbo-mercure-stream-source>

3. Progressive Enhancement

Pattern: Add Turbo attributes to existing links/forms for instant transitions. Example:

<a href="{{ path('app_show_post', { id: post.id }) }}"
   data-turbo="true"
   data-turbo-action="advance">
    View Post
</a>

Fallback: If JavaScript is disabled, the link behaves as a normal <a> tag.


4. Form Submissions with Turbo

Pattern: Use data-turbo-frame to update a frame after submission. Example:

<form data-turbo-frame="form_result">
    {# Form fields #}
    <button type="submit">Submit</button>
</form>

<turbo-frame id="form_result"></turbo-frame>

Controller:

public function submitForm(Request $request): Response
{
    // Process data...
    return $this->render('form/_result.html.twig');
}

5. Custom Turbo Actions

Pattern: Extend Turbo’s behavior with custom actions (e.g., data-turbo-action="scroll-to"). JavaScript:

// assets/controllers/turbo_custom_controller.js
import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
    connect() {
        this.element.addEventListener('turbo:load', () => {
            window.scrollTo(0, 0);
        });
    }
}

Usage in Twig:

<div data-controller="turbo-custom" data-turbo-action="scroll-to">
    {# Content #}
</div>

6. Integration with Symfony Mercure

Pattern: Broadcast updates to all connected clients. Setup:

  1. Install symfony/mercure-bundle.
  2. Configure Mercure in config/packages/mercure.yaml:
    mercure:
        hubs:
            default:
                url: '%env(MERCURE_URL)%'
                public_url: '%env(MERCURE_PUBLIC_URL)%'
                jwt: '%kernel.project_dir%/var/mercure.jwt'
    
  3. Use turbo_stream_from() in Twig:
    {{ turbo_stream_from('https://example.com/.well-known/mercure') }}
    

Gotchas and Tips

Pitfalls

  1. Missing Accept Header:

    • Issue: Turbo Streams require Accept: text/vnd.turbo-stream.html.
    • Fix: Ensure your controller checks TurboBundle::STREAM_FORMAT:
      if (TurboBundle::STREAM_FORMAT === $request->getPreferredFormat()) {
          $request->setRequestFormat(TurboBundle::STREAM_FORMAT);
      }
      
  2. CORS Errors with Mercure:

    • Issue: Turbo Mercure streams may fail if CORS is misconfigured.
    • Fix: Configure Mercure’s public_url and ensure the client’s origin is allowed:
      mercure:
          hubs:
              default:
                  public_url: 'https://mercure.example.com/.well-known/mercure'
                  allowed_origins: ['https://your-app.com']
      
  3. Turbo Frame ID Conflicts:

    • Issue: Dynamic frame IDs (e.g., user_avatar_{% user.id %}) may cause conflicts if not scoped properly.
    • Fix: Use unique namespaces (e.g., user_avatar_123 vs. profile_avatar_123).
  4. JavaScript Conflicts:

    • Issue: Turbo may interfere with other JS libraries (e.g., jQuery plugins).
    • Fix: Use data-turbo="false" on problematic elements or wrap them in data-turbo-frame with unique IDs.
  5. Deprecated turbo_stream_listen:

    • Issue: Older code uses turbo_stream_listen(), which is deprecated.
    • Fix: Replace with turbo_stream_from() or <turbo-mercure-stream-source>:
      {# Old #}
      {{ turbo_stream_listen('https://example.com/updates') }}
      
      {# New #}
      {{ turbo_stream_from('https://example.com/updates') }}
      
  6. Twig Components in Streams:

    • Issue: Turbo Streams may not render Twig components correctly.
    • Fix: Use {{ component('Turbo:Stream', { ... }) }} or inline HTML.

Debugging Tips

  1. Check Network Tab:

    • Look for text/vnd.turbo-stream.html responses in Chrome DevTools.
    • Verify Turbo-Frame-Options headers for frame requests.
  2. Enable Turbo Logs: Add to config/packages/dev/turbo.yaml:

    turbo:
        debug: true
    
  3. Test Frame Updates: Use data-turbo-action="replace" to force a frame update:

    <a href="{{ path('app_update') }}" data-turbo-action="replace">Update</a>
    
  4. Mercure Debugging:

    • Check Mercure hub logs for subscription errors.
    • Use curl to test the hub:
      curl -H "Authorization: Bearer YOUR_JWT" https://merc
      
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.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui