DOM Manipulation

Update the browser's DOM directly from your controllers.

Overview

Gale can send HTML from your server and intelligently update the DOM. You have complete control over:

  • What to send — Blade views, fragments, or raw HTML
  • Where to update — Target specific elements with CSS selectors
  • How to update — Choose from 9 different patching modes

Alpine State Preservation

Gale uses Alpine's morphing algorithm by default, which intelligently updates the DOM while preserving Alpine component state, focus, and scroll position.

Rendering Views

Basic View Rendering

Render a Blade view and send it to the browser:

1return gale()->view('partials.user-card', ['user' => $user]);

Targeting Specific Elements

Use a selector to target specific elements:

1return gale()->view('partials.user-card', ['user' => $user], [
2    'selector' => '#user-profile',
3]);

View Options

1return gale()->view('partials.list-item', ['item' => $item], [
2    'selector' => '#items-list',     // CSS selector for target element
3    'mode' => 'append',              // How to apply the update
4    'useViewTransition' => true,    // Use View Transitions API
5    'settle' => 100,                // Delay in ms for CSS transitions
6    'limit' => 5,                   // Max elements to update
7]);

Raw HTML

For simple updates, send raw HTML strings:

1return gale()->html('<span class="badge">New!</span>', [
2    'selector' => '#notification-badge',
3    'mode' => 'replace',
4]);
Escape User Input
Always escape user-provided content with e() or use Blade views to prevent XSS attacks.

DOM Patching Modes

Gale supports 9 different modes for patching the DOM:

Mode Aliases Behavior Alpine State
outer outerHTML, replace Replace element with new content Lost
inner innerHTML Replace element's children Lost
outerMorph morph Smart replace with intelligent diffing (default) Preserved
innerMorph morph_inner Smart replace children with diffing Preserved
before beforebegin Insert before element N/A (new element)
after afterend Insert after element N/A (new element)
prepend afterbegin Insert as first child N/A (new element)
append beforeend Insert as last child N/A (new element)
remove delete Delete element from DOM with cleanup Removed

Morph Modes vs Direct Replacement

Morph modes (outerMorph and innerMorph) use Alpine's morphing algorithm to intelligently diff the new HTML against the existing DOM. This preserves:

  • Alpine component state (x-data)
  • Focus state on inputs
  • Scroll position
  • CSS transitions in progress

Direct replacement modes (outer and inner) are faster but destroy existing state. Use them for static content or when you want a clean slate.

Default Mode
The default mode is outerMorph (alias: morph). You rarely need to change it unless you have specific requirements.

Convenience Methods

Gale provides shorthand methods for common operations:

append() / prepend()

1// Add item to end of list
2return gale()->append('#todo-list', view('partials.todo-item', ['todo' => $todo]));
3
4// Add item to beginning of list
5return gale()->prepend('#notifications', '<div class="notification">New message</div>');

before() / after()

1// Insert before an element
2return gale()->before('#item-5', view('partials.item', ['item' => $newItem]));
3
4// Insert after an element
5return gale()->after('.header', '<div class="alert">Important notice</div>');

inner() / outer()

1// Replace only the inner content (uses innerMorph mode)
2return gale()->inner('#container', view('partials.content', $data));
3
4// Replace the entire element including itself (uses outerMorph mode)
5return gale()->outer('#old-element', view('partials.new-element', $data));

replace() / remove()

1// Quick replacement (no morphing)
2return gale()->replace('#status', '<span class="online">Online</span>');
3
4// Remove element from DOM
5return gale()->remove('#deleted-item-' . $id);

Multiple Updates

Chain multiple DOM operations in a single response:

1return gale()
2    ->state('loading', false)
3    ->view('partials.user-list', compact('users'), [
4        'selector' => '#user-list',
5    ])
6    ->append('#activity-log', view('partials.log-entry', [
7        'action' => 'Users loaded',
8    ]))
9    ->remove('#loading-spinner');

Each update is sent as a separate SSE event and applied in order.

Practical Examples

Infinite Scroll

 1public function loadMore()
 2{
 3    $page = request()->state('page', 1);
 4    $items = Item::paginate(10, ['*'], 'page', $page);
 5
 6    return gale()
 7        ->state('page', $page + 1)
 8        ->state('hasMore', $items->hasMorePages())
 9        ->append('#items-container', view('partials.items', compact('items')));
10}

Toast Notification

 1public function save()
 2{
 3    // ... save logic ...
 4
 5    return gale()
 6        ->state('saved', true)
 7        ->append('#toast-container', view('partials.toast', [
 8            'message' => 'Changes saved successfully!',
 9            'type' => 'success',
10        ]));
11}

Delete Item

1public function destroy(Item $item)
2{
3    $item->delete();
4
5    return gale()
6        ->state('count', Item::count())
7        ->remove('#item-' . $item->id);
8}

Quick Reference

Method Description
view($view, $data, $options) Render Blade view and send to DOM
html($html, $options) Send raw HTML to DOM
append($selector, $html) Append as last child
prepend($selector, $html) Prepend as first child
before($selector, $html) Insert before element
after($selector, $html) Insert after element
inner($selector, $html) Replace inner content (innerMorph mode)
outer($selector, $html) Replace entire element (outerMorph mode)
replace($selector, $html) Quick replacement (outer mode, no morph)
remove($selector) Remove element from DOM

On this page