Boson

Turbo

Boson works with Hotwire Turbo out of the box — no configuration required.


Overview

If your app uses Hotwire Turbo, Boson's built-in lifecycle system takes care of component initialization and cleanup for you. There's nothing to configure — just import Boson and everything works.

This covers Turbo Drive (full-page navigations), Turbo Frames (partial page updates), and Turbo Streams (server-pushed DOM changes).

How It Works

Boson components use data-controller attributes to identify themselves in the DOM. The lifecycle system watches for these elements and manages their JavaScript automatically:

Turbo Drive

When Turbo replaces the page body during navigation, Boson listens for two events:

This prevents duplicate event listeners and stale state that would otherwise accumulate across Turbo navigations.

Turbo Frames & Streams

For content injected via Turbo Frames or Streams, Boson uses a MutationObserver that watches the entire document. When new elements with data-controller appear in the DOM, they're initialized automatically. When elements are removed, their components are cleaned up.

This means you can freely use Boson components inside Turbo Frames and they'll work exactly as expected — dropdowns, modals, forms, and everything else.

{{-- A Turbo Frame that loads new content with Boson components --}}
<turbo-frame id="notifications">
    {{-- Any Boson components inside here (modals, dropdowns, forms, etc.) --}}
    {{-- are automatically initialized when the frame loads --}}
    {{-- and cleaned up when the frame is replaced --}}
    <x-boson::dropdown>
        <x-boson::dropdown.trigger>
            <x-boson::button icon="bell">Notifications</x-boson::button>
        </x-boson::dropdown.trigger>
        <x-boson::dropdown.menu>
            <x-boson::dropdown.item>New message</x-boson::dropdown.item>
        </x-boson::dropdown.menu>
    </x-boson::dropdown>
</turbo-frame>

Setup

There's nothing to configure. If Turbo is present on the page, Boson responds to its events. If Turbo isn't present, Boson simply initializes on DOMContentLoaded. The same import works either way:

// resources/js/app.js
import '../../vendor/davidgut/boson/resources/js/boson.js';

// That's it. Works with or without Turbo.

Turbo Attributes

Several Boson components accept a turbo:* prefix to pass Turbo data attributes. Each prefixed prop maps to a data-turbo-* attribute on the rendered element.

{{-- turbo:frame="main" → data-turbo-frame="main" --}}
<x-boson::link href="/users" turbo:frame="main">Users</x-boson::link>

{{-- turbo:action="replace" → data-turbo-action="replace" --}}
<x-boson::link href="/settings" turbo:action="replace">Settings</x-boson::link>

{{-- Multiple turbo attributes --}}
<x-boson::button turbo:confirm="Are you sure?" turbo:submits-with="Saving...">Save</x-boson::button>

The following components support the turbo:* prefix:

Opting Out

All of the components above default to turbo="true". To disable Turbo on a specific element, set :turbo="false". This outputs data-turbo="false" on the rendered HTML.

{{-- Disable Turbo on a form (use standard browser submission) --}}
<x-boson::form :turbo="false" action="/upload" method="POST">
    ...
</x-boson::form>

{{-- Disable Turbo on a link --}}
<x-boson::link :turbo="false" href="/download/report.pdf">Download PDF</x-boson::link>

{{-- Disable Turbo on a button rendered as a link --}}
<x-boson::button :turbo="false" as="a" href="/external">External Link</x-boson::button>

Confirmation Dialogs

Use turbo:confirm to show a browser confirmation dialog before Turbo processes the element. Pair with turbo:submits-with on buttons to change the button text while submitting.

{{-- Confirm before submitting --}}
<x-boson::form action="/teams/1" method="DELETE" turbo:confirm="Delete this team?">
    <x-boson::button type="submit" variant="danger" turbo:submits-with="Deleting...">
        Delete Team
    </x-boson::button>
</x-boson::form>

{{-- Confirm on a standalone button --}}
<x-boson::button turbo:confirm="Are you sure?">Archive</x-boson::button>