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

Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require symfony/ux-turbo
    

    Add the bundle to config/bundles.php:

    return [
        // ...
        Symfony\UX\Turbo\TurboBundle::class => ['all' => true],
    ];
    
  2. Enable Turbo in Twig Add to your base template (templates/base.html.twig):

    {% block stylesheets %}
        {{ parent() }}
        {{ encore_entry_link_tags('app') }}
        {{ turbo_stylesheets() }}
    {% endblock %}
    
    {% block javascripts %}
        {{ parent() }}
        {{ encore_entry_link_tags('app') }}
        {{ turbo_scripts() }}
    {% endblock %}
    
  3. First Turbo Drive Use Case Replace a traditional form submission with Turbo:

    <form turbo-frame="result-frame">
        <input type="text" name="query">
        <button type="submit">Search</button>
    </form>
    
    <turbo-frame id="result-frame">
        {% block result %}{% endblock %}
    </turbo-frame>
    

    Controller:

    public function search(Request $request, Response $response): Response
    {
        $query = $request->request->get('query');
        return $this->render('partials/_search_results.html.twig', [
            'results' => $this->searchService->find($query),
        ]);
    }
    

Implementation Patterns

Common Workflows

  1. Progressive Enhancement

    • Start with traditional server-rendered pages.
    • Gradually replace full-page loads with Turbo frames:
      <!-- Before -->
      <a href="/dashboard">Dashboard</a>
      
      <!-- After -->
      <a href="/dashboard" data-turbo-frame="_top">Dashboard</a>
      
  2. Frame-Based Navigation

    • Use turbo-frame for partial updates:
      <turbo-frame id="sidebar">
          {% include 'partials/_sidebar.html.twig' %}
      </turbo-frame>
      
    • Update frames via controller:
      return $this->render('partials/_sidebar.html.twig', [...]);
      
  3. Streaming Responses

    • For long-running tasks (e.g., file uploads):
      $response = new StreamingResponse();
      $response->setCallback(function () use ($task) {
          while (!$task->isComplete()) {
              sleep(1);
              echo $this->renderView('partials/_progress.html.twig', [
                  'progress' => $task->getProgress(),
              ]);
          }
      });
      return $response;
      
  4. Integration with Symfony UX

    • Combine with Stimulus for interactivity:
      // assets/controllers/auto_submit_controller.js
      import { Controller } from '@hotwired/stimulus';
      
      export default class extends Controller {
          connect() {
              this.element.addEventListener('submit', (e) => {
                  e.preventDefault();
                  this.element.requestSubmit();
              });
          }
      }
      
      <form data-controller="auto-submit" turbo-frame="result">
          <!-- ... -->
      </form>
      
  5. Fallback Handling

    • Use data-turbo-permanent for critical frames:
      <turbo-frame id="header" data-turbo-permanent>
          {% include 'partials/_header.html.twig' %}
      </turbo-frame>
      

Integration Tips

  1. Asset Management

    • Ensure Turbo’s JS is loaded after your app’s JS (use turbo_scripts() in the right block).
    • For Webpack Encore, add to webpack.config.js:
      Encore
          .addEntry('turbo', 'turbo')
          .splitEntry('turbo');
      
  2. Routing

    • Prefix Turbo routes with _ to avoid conflicts:
      # config/routes.yaml
      _turbo:
          path: /turbo/{action}
          controller: App\Controller\TurboController::index
      
  3. Caching

    • Use Vary: Turbo-Frame headers for frame-specific caching:
      $response->headers->set('Vary', 'Turbo-Frame');
      
  4. Testing

    • Use Turbo::drive() in PHPUnit:
      public function testTurboFrame(): void
      {
          $client = static::createClient();
          $client->request('GET', '/');
          $client->followRedirects();
      
          $crawler = Turbo::drive($client)->clickLink('Dashboard');
          $this->assertSelectorTextContains('turbo-frame#result', 'Welcome');
      }
      

Gotchas and Tips

Pitfalls

  1. Double Submissions

    • Turbo resubmits forms on page reload. Prevent with:
      <form data-turbo="false">
          <!-- or -->
      <form data-turbo-action="replace">
      
  2. CSRF Tokens

    • Ensure tokens are included in Turbo frames:
      <turbo-frame id="form-frame">
          {{ form_start(form, {'attr': {'data-turbo-frame': '_top'}}) }}
      
  3. History API Conflicts

    • Turbo manipulates the history stack. For SPAs, disable with:
      document.addEventListener('turbo:before-visit', (event) => {
          if (event.detail.url.includes('/spa')) {
              event.preventDefault();
          }
      });
      
  4. Asset Fingerprinting

    • Turbo’s JS/CSS may break fingerprinting. Use:
      {{ turbo_stylesheets({ 'filter': 'ignore' }) }}
      
  5. Server-Side Rendering (SSR) Issues

    • Avoid mixing Turbo with SSR frameworks (e.g., React Hydration). Use data-turbo="false" on SSR components.

Debugging

  1. Turbo Logs

    • Enable debug mode in config/packages/symfony_ux_turbo.yaml:
      turbo:
          debug: true
      
    • Check browser console for turbo:... events.
  2. Network Tab

    • Look for X-Turbo-Frame headers in responses. Missing headers often indicate routing issues.
  3. Frame Isolation

    • Use data-turbo-frame="external" for cross-origin frames (requires CORS headers).

Extension Points

  1. Custom Events

    • Listen to Turbo lifecycle events in JS:
      document.addEventListener('turbo:load', () => {
          console.log('Page loaded via Turbo');
      });
      
  2. Middleware

    • Add Turbo-specific logic in middleware:
      public function handle(Request $request, Closure $next): Response
      {
          if ($request->headers->has('Turbo-Frame')) {
              // Custom frame logic
          }
          return $next($request);
      }
      
  3. Adapters

    • Extend Turbo’s behavior by creating custom adapters (e.g., for API-driven apps):
      use Symfony\UX\Turbo\Adapter\TurboAdapterInterface;
      
      class ApiTurboAdapter implements TurboAdapterInterface
      {
          public function render(Response $response): string
          {
              return json_encode($response->getContent());
          }
      }
      
  4. Stimulus Integration

    • Use Turbo’s turbo:submit-start to show loaders:
      document.addEventListener('turbo:submit-start', (event) => {
          event.target.querySelector('.spinner').classList.remove('hidden');
      });
      
  5. Progressive Hydration

    • For SPAs, use Turbo to hydrate critical sections:
      <div id="app" data-turbo="false">
          {{ include('partials/_turbo_hydrated.html.twig') }}
      </div>
      
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.
comsave/common
alecsammon/php-raml-parser
chrome-php/wrench
lendable/composer-license-checker
typhoon/reflection
mesilov/moneyphp-percentage
mike42/gfx-php
bookdown/themes
aura/view
aura/html
aura/cli
povils/phpmnd
nayjest/manipulator
omnipay/tests
psr-mock/http-message-implementation
psr-mock/http-factory-implementation
psr-mock/http-client-implementation
voku/email-check
voku/urlify
rtheunissen/guzzle-log-middleware