v1 LiVue v1 is here — server-driven reactivity for Laravel using Vue.js Get Started →

Directives

Custom Vue directives provided by LiVue for server interaction, timing control, and declarative UI behavior.

v-click

A clean, declarative syntax for calling server-side methods. Instead of writing @click="method()", you can use v-click="method" directly on any element.

Basic Usage

template.blade.php
<!-- Call a method with no arguments -->
<button v-click="increment">+1</button>

<!-- Pass a single argument -->
<button v-click="['delete', {{ item.id }}]">Delete</button>

<!-- Pass multiple arguments as an array -->
<button v-click="['update', {{ item.id }}, 'active']">Activate</button>

<!-- Pass a string argument -->
<button v-click="['setStatus', 'published']">Publish</button>

Modifiers

Modifier Example Description
.prevent v-click.prevent="save" Calls event.preventDefault()
.stop v-click.stop="handle" Calls event.stopPropagation()
.self v-click.self="close" Only triggers if the event target is the element itself
.once v-click.once="init" Handler fires only once, then is removed
.outside v-click.outside="close" Triggers when clicking outside the element
.debounce v-click.debounce.500ms="save" Debounces the action (default 250ms, customizable)
.throttle v-click.throttle.1000ms="track" Throttles the action (default 250ms, customizable)
.capture v-click.capture="handle" Listens in capture phase instead of bubbling
.passive v-click.passive="scroll" Passive mode, does not block scroll performance

Combining Modifiers

Modifiers can be chained together on a single directive.

<!-- Prevent default and stop propagation -->
<a href="#" v-click.prevent.stop="submit">Submit</a>

<!-- Close modal on overlay click, not on content click -->
<div class="overlay" v-click.self.prevent="close">
    <div class="modal">Content</div>
</div>

<!-- Close dropdown on first outside click -->
<div v-click.outside.once="dismiss">
    Dropdown content
</div>

Calling Server Methods

There are three ways to call server methods. All produce the same result:

v-click (declarative)
<button v-click="save">Save</button>
@click (direct call)
<button @click="save()">Save</button>

Use v-click for simple calls and @click for multiple arguments or inline logic. Use v-click modifiers for debounce/throttle.

Direct Calls with @click

Server methods are available directly in your template — no prefix needed. Use them with any Vue event handler:

template.blade.php
<!-- No arguments -->
<button @click="save()">Save</button>

<!-- With arguments -->
<button @click="delete({{ item.id }})">Delete</button>

<!-- Multiple arguments -->
<button @click="update({{ item.id }}, 'active')">Activate</button>

<!-- With inline logic -->
<button @click="if (confirm) archive({{ item.id }})">Archive</button>

<!-- Any Vue event, not just click -->
<input @change="toggleItem({{ item.id }})" type="checkbox">

Importantv-click supports method identifiers, call expressions, and array syntax. Call expressions (for example v-click="setPage(page)") are deferred and executed on the click event.

<!-- All valid -->
<button v-click="save">                         <!-- Identifier -->
<button v-click="['save', {{ item.id }}]">              <!-- Array args -->
<button v-click="setPage({{ page }})">          <!-- Call expression -->
<button @click="if (confirm) archive({{ item.id }})"> <!-- Inline logic -->

When to Use Each Syntax

Syntax Best for
v-click="method" Simple server calls with optional modifiers (.debounce, .throttle, .confirm).
v-click="method(arg1, arg2)" Server calls with explicit arguments while keeping the same v-click modifiers.
@click="method()" Inline logic or client-side branches, and for any Vue event (@change, @input, etc.).

v-model

Two-way binding between form inputs and your component's public properties. Standard Vue v-model works out of the box with input, textarea, select, checkboxes, and radio buttons. LiVue extends it with timing modifiers for server synchronization.

Basic Usage

template.blade.php
<!-- Text input -->
<input v-model="name" type="text" />

<!-- Textarea -->
<textarea v-model="description"></textarea>

<!-- Select -->
<select v-model="country">
    <option value="us">United States</option>
    <option value="uk">United Kingdom</option>
</select>

<!-- Checkbox -->
<input v-model="agreed" type="checkbox" />

LiVue Modifiers

These modifiers control when and how the input value is synchronized with the server. They work with both native HTML inputs and Vue components (such as Vuetify).

Modifier Default Description
.debounce 150ms Delays sync until user stops typing. Customizable: .debounce.500ms
.throttle 150ms Limits sync to at most once per interval. Customizable: .throttle.300ms
.blur Syncs only when the input loses focus
.enter Syncs only when the user presses Enter
template.blade.php
<!-- Debounce 500ms for search inputs -->
<input v-model.debounce.500ms="search" placeholder="Search..." />

<!-- Throttle 300ms for frequently typed inputs -->
<input v-model.throttle.300ms="liveValue" />

<!-- Sync only on blur -->
<input v-model.blur="username" />

<!-- Sync only on Enter -->
<input v-model.enter="command" />

Under the hood, LiVue transforms v-model.debounce.500ms="search" into v-model="search" v-debounce:search.500ms at runtime, so the standard Vue binding is preserved while the modifier directive handles timing.

v-loading

Show or hide elements, or toggle CSS classes, while an AJAX request is in progress. This is useful for spinners, skeleton screens, and disabling interactive elements during server calls.

Show While Loading

By default, elements with v-loading are hidden and become visible only during a request.

template.blade.php
<!-- Show spinner while any request is in progress -->
<div v-loading>
    Loading...
</div>

<!-- Hide element while loading -->
<div v-loading.remove>
    This content disappears during requests
</div>

Target Specific Actions

You can scope the loading state to a specific server method using the .action modifier.

<!-- Show only when 'save' is running -->
<span v-loading.action="'save'">Saving...</span>

<!-- Show only when 'delete' is running -->
<span v-loading.action="'delete'">Deleting...</span>

<button v-click="save">Save</button>
<button v-click="['delete', {{ item.id }}]">Delete</button>

CSS Class Toggling

Instead of showing or hiding elements, you can toggle CSS classes during loading. This is useful for reducing opacity or adding visual cues without removing elements from the page.

<!-- Add 'opacity-50' class while loading -->
<div v-loading.class="'opacity-50 pointer-events-none'">
    Content dims while saving
</div>

<!-- Add class only during a specific action -->
<button v-click="save" v-loading.class.action="'opacity-50'">
    Save
</button>

v-poll

Automatically refresh component data at regular intervals. Useful for dashboards, notifications, and any UI that should stay current without user interaction.

template.blade.php
<!-- Refresh component every 2.5s (default) -->
<div v-poll>
    {{ notifications.length }} new notifications
</div>

<!-- Poll every 5 seconds, calling a specific method -->
<div v-poll.5s="'refreshData'">
    ...
</div>

<!-- Poll in milliseconds -->
<div v-poll.500ms="'checkStatus'">
    ...
</div>

<!-- Poll only when element is visible in the viewport -->
<div v-poll.3s.visible="'refresh'">
    ...
</div>

<!-- Keep polling even when the browser tab is inactive -->
<div v-poll.2s.keep-alive>
    ...
</div>
Modifier Description
.Xs Interval in seconds (e.g. .5s, .10s)
.Xms Interval in milliseconds (e.g. .500ms)
.visible Only poll when the element is visible in the viewport (IntersectionObserver)
.keep-alive Continue polling when the browser tab is inactive

By default, polling pauses automatically when the tab is hidden to reduce unnecessary server requests. If no method is specified, the component calls $refresh, which re-renders the entire component.

v-init

Call a server method when the component is first mounted. This is useful for deferred data loading — the initial HTML renders instantly, and the heavy data is fetched asynchronously afterward.

template.blade.php
<!-- Load data when the component mounts -->
<div v-init:loadData>
    <div v-loading>Loading...</div>
    <div v-loading.remove>
        <ul>
            <li v-for="item in items">{{ item.name }}</li>
        </ul>
    </div>
</div>

The method is called once, immediately after Vue mounts the component. Combine it with v-loading to show a placeholder while the data is being fetched.

v-submit

A form submission handler that calls a server method when the form is submitted. It automatically prevents the default browser submission, so you do not need to add .prevent manually.

template.blade.php
<form v-submit:save>
    <input v-model="name" type="text" />
    <input v-model="email" type="email" />

    <button type="submit">Save</button>
</form>

When the user submits the form (via button click or pressing Enter), the save() method on your PHP component is called with all bound properties automatically synced.

v-intersect

Triggers a server method when an element enters the viewport. Built on the browser's IntersectionObserver API. Ideal for infinite scrolling, lazy loading, and analytics triggers.

template.blade.php
<!-- Infinite scroll: load more items when this element becomes visible -->
<ul>
    <li v-for="item in items">{{ item.name }}</li>
</ul>

<div v-intersect:loadMore>
    <span v-loading>Loading more...</span>
</div>

<!-- Trigger only once (e.g. analytics) -->
<div v-intersect:trackView.once>
    Section content
</div>

<!-- Trigger when element is 50% visible -->
<div v-intersect:animate.half>
    ...
</div>

<!-- Trigger when element is fully visible -->
<div v-intersect:markRead.full>
    ...
</div>
Modifier Description
.once Fire the method only once, then stop observing
.half Trigger when 50% of the element is visible
.full Trigger when 100% of the element is visible

v-navigate

Enables SPA-style navigation for anchor links. When added to an <a> tag, it prevents a full page reload. Instead, LiVue fetches the new page content via AJAX and swaps it into the DOM, preserving any

state across pages.

template.blade.php
<!-- Just add v-navigate to any anchor -->
<a href="/dashboard" v-navigate>Dashboard</a>

<!-- Works with dynamic hrefs -->
<a :href="'/users/' + user.id" v-navigate>
    {{ user.name }}
</a>

<!-- Navigation menu -->
<nav>
    <a href="/docs" v-navigate>Docs</a>
    <a href="/examples" v-navigate>Examples</a>
    <a href="/about" v-navigate>About</a>
</nav>

The browser URL, history, and scroll position are updated automatically. Components wrapped in

(like sidebars, media players, or chat widgets) maintain their state across navigations.

v-current

Adds an active CSS class to an anchor element when its href matches the current URL. Useful for highlighting the active page in navigation menus.

template.blade.php
<!-- Adds 'text-white font-bold' when the URL starts with /docs -->
<a href="/docs" v-navigate v-current="'text-white font-bold'">
    Docs
</a>

<!-- .exact: only match when the URL is exactly /docs -->
<a href="/docs" v-navigate v-current.exact="'text-white font-bold'">
    Docs Home
</a>

<!-- Object syntax: toggle between active and inactive classes -->
<a href="/docs" v-navigate
   v-current.exact="{ active: 'text-white bg-vue/20', inactive: 'text-gray-400 hover:text-white' }">
    Docs
</a>
Modifier Description
.exact Only apply the class when the URL matches exactly, not just as a prefix
.strict Like .exact but considers trailing slashes as different paths
Value Description
'classes' String of CSS classes added when active, removed when inactive
{ active, inactive } Object with active and inactive class strings that toggle based on match state

Without .exact, a link to /docs will also be marked active when the URL is /docs/v1/directives. With .exact, only an exact path match activates the class.

Active links automatically receive aria-current="page" for accessibility. The directive uses global event delegation, so it works correctly inside

containers.

v-transition

Integrates with the browser's View Transitions API to add smooth animations when component content changes. When the server re-renders a component, the old and new states are animated automatically.

template.blade.php
<!-- Auto-generated transition name -->
<div v-transition>
    Step {{ currentStep }} content
</div>

<!-- Named transition for custom CSS animations -->
<div v-transition="'step-content'">
    ...
</div>

<!-- Skip transition for this element -->
<div v-transition.skip>
    This element will not animate
</div>

Custom CSS Animations

When you give a transition a name, you can target it with CSS pseudo-elements for full control over the animation.

styles.css
::view-transition-old(step-content) {
    animation: slide-out-left 0.25s ease-out;
}

::view-transition-new(step-content) {
    animation: slide-in-right 0.25s ease-in;
}

LiVue includes default fade and slide animations. The directive automatically respects prefers-reduced-motion and falls back gracefully in browsers that do not support the View Transitions API.

v-scroll

Preserves and restores the scroll position of an element across re-renders and navigations. Useful for sidebars, scrollable lists, and any container that should maintain its position when the page updates.

template.blade.php
<!-- Preserve scroll position with an identifier -->
<div class="h-96 overflow-y-auto" v-scroll="'sidebar-list'">
    <ul>
        <li v-for="item in items">{{ item.name }}</li>
    </ul>
</div>

<!-- Sidebar navigation that keeps scroll position -->
<aside class="overflow-y-auto" v-scroll="'docs-sidebar'">
    ...
</aside>

The string identifier is used to store and restore the position. Different elements with the same identifier share the same scroll state. This works across component re-renders and SPA navigations via v-navigate.

v-dirty

Shows or hides elements based on whether the component has unsaved changes. A component is considered "dirty" when its client-side state differs from the last server-confirmed state.

template.blade.php
<!-- Show "unsaved changes" indicator when dirty -->
<span v-dirty>
    You have unsaved changes
</span>

<!-- Hide element when dirty -->
<span v-dirty:remove>
    All changes saved
</span>

<!-- Track a specific property -->
<span v-dirty="'name'">
    Name has been modified
</span>

Use v-dirty to display save prompts, enable save buttons, or highlight changed fields. The :remove variant inverts the behavior, hiding the element when there are unsaved changes.

v-watch

Syncs a reactive property to the server whenever its value changes. This is useful for properties bound with v-model that need server-side processing on every change, such as triggering watchers, validation, or dependent field updates.

template.blade.php
<!-- Sync when the property changes (debounced 500ms by default) -->
<div v-watch="'data.name'">
    <input v-model="data.name" />
</div>

<!-- Custom debounce timing -->
<div v-watch.debounce.300ms="'data.email'">
    <input v-model="data.email" />
</div>

<!-- Sync only on blur -->
<div v-watch.blur="'data.username'">
    <input v-model="data.username" />
</div>
Modifier Description
.debounce.Xms Custom debounce timing (default is 500ms)
.blur Sync only when the element loses focus

The path supports dot-notation for nested properties (e.g. data.section.title). When the watched property changes, livue.sync() is called to send the state diff to the server.

v-offline

Shows content or modifies elements when the user's device goes offline. Built on the browser's navigator.onLine property and online/offline events.

template.blade.php
<!-- Show a banner when offline -->
<div v-offline class="bg-red-600 text-white p-4">
    You are currently offline. Changes will sync when you reconnect.
</div>

<!-- Hide element when offline (e.g. hide a save button) -->
<button v-offline:remove v-click="save">
    Save
</button>

<!-- Add a class when offline -->
<div v-offline.class="'opacity-50 pointer-events-none'">
    This section becomes dimmed when offline
</div>

<!-- Disable a button when offline -->
<button v-offline.attr="'disabled'">Submit</button>
Modifier Description
(default) Toggle visibility: hidden when online, visible when offline
:remove Inverse visibility: visible when online, hidden when offline
.class Add the specified CSS classes when offline
.class.remove Remove the specified CSS classes when offline
.attr Add the specified HTML attribute when offline

v-sort

Drag-and-drop reordering for lists, powered by SortableJS. Supports server-side and client-side modes, drag handles, cross-list transfers (Kanban), and touch devices.

Server-Side (Calls PHP)

When the value is a string, the drop event calls the named PHP method on the server.

template.blade.php
<ul v-sort="'reorder'">
    <li v-for="item in items" :key="item.id" v-sort-item="item.id">
        {{ item.name }}
    </li>
</ul>
Component.php
public function reorder(int|string $item, int $position): void
{
    // $item = the ID from v-sort-item
    // $position = new 0-based index
}

Client-Side (No Server Call)

When the value is an array reference (no quotes), the reordering happens locally in Vue without calling the server. The changes are sent on the next sync.

<div v-sort="data.gallery">
    <div v-for="(file, index) in data.gallery" :key="index" v-sort-item="index">
        {{ file.name }}
    </div>
</div>

Handles and Ignored Elements

<li v-sort-item="item.id">
    <!-- Only this handle initiates the drag -->
    <span v-sort-handle class="cursor-grab">&#x2630;</span>
    <span>{{ item.name }}</span>
    <!-- This button won't trigger drag -->
    <button v-sort-ignore v-click="['delete', item.id]">Delete</button>
</li>

Cross-List (Kanban)

Items can be dragged between lists that share the same v-sort-group.

<!-- TODO list -->
<ul v-sort="'reorderTodo'" v-sort-group="'tasks'" data-livue-sort-id="todo">
    <li v-for="task in todoTasks" :key="task.id" v-sort-item="task.id">
        {{ task.name }}
    </li>
</ul>

<!-- DONE list -->
<ul v-sort="'reorderDone'" v-sort-group="'tasks'" data-livue-sort-id="done">
    <li v-for="task in doneTasks" :key="task.id" v-sort-item="task.id">
        {{ task.name }}
    </li>
</ul>

Directives Reference

Directive Description
v-sort Sortable container. String value = server call, array ref = client-side
v-sort-item Marks a sortable item with its ID or index
v-sort-handle Restricts drag initiation to this element
v-sort-ignore Excludes this element from triggering a drag
v-sort-group Group name for cross-list dragging

Modifiers

Modifier Description
.Xms Animation duration (e.g. .300ms)
.no-animation Disable animation entirely
.horizontal Horizontal sorting axis

v-ignore

Prevents LiVue from updating the content of an element during server re-renders. The element's inner HTML is preserved as-is. This is essential for third-party widgets, manually managed DOM, or any content that should not be touched by the template swap.

template.blade.php
<!-- Third-party widget that manages its own DOM -->
<div v-ignore>
    <div id="chart-container"></div>
</div>

<!-- WYSIWYG editor content -->
<div v-ignore>
    <div class="rich-editor"></div>
</div>

<!-- Embedded map that should not be re-rendered -->
<div v-ignore>
    <div id="map"></div>
</div>

Without v-ignore, LiVue replaces the component template on every server response. Any DOM modifications made by JavaScript libraries (charts, editors, maps) would be lost. This directive marks a subtree as protected from the swap.

Action Modifiers

Use v-click modifiers to add debounce or throttle to server method calls.

<!-- Debounce 300ms -->
<button v-click.debounce.300ms="search">Search</button>

<!-- Throttle 500ms -->
<button v-click.throttle.500ms="increment">+1</button>

How They Differ

Debounce

Waits for a pause in activity. Each new call resets the timer. Only the last call in the series executes. Best for search inputs and auto-save.

Throttle

Executes immediately, then ignores subsequent calls for the cooldown period. Best for rate-limiting repeated actions like button clicks or scroll tracking.