Vue - v1.0.0

Migration from Livewire

This guide helps you migrate from ArtisanPack UI Livewire components (artisanpack-ui/livewire-ui-components) to the Vue equivalents (@artisanpack-ui/vue).

Overview

The Vue components mirror the Livewire component API as closely as possible. The main differences are:

  • Naming: Blade <x-artisanpack-button> becomes Vue <Button>
  • Data binding: wire:model becomes v-model
  • Events: wire:click becomes @click
  • Slots: Blade named slots (<x-slot:header>) become Vue named slots (<template #header>)
  • Server state: Livewire properties become Vue refs or Inertia form state

Component Mapping

Livewire (Blade) Vue Import Notes
<x-artisanpack-button> Button Same props
<x-artisanpack-input> Input Same props
<x-artisanpack-select> Select Same props
<x-artisanpack-textarea> Textarea Same props
<x-artisanpack-checkbox> Checkbox Same props
<x-artisanpack-toggle> Toggle Same props
<x-artisanpack-radio> Radio Same props
<x-artisanpack-card> Card Same props and slots
<x-artisanpack-modal> Modal wire:model becomes v-model
<x-artisanpack-alert> Alert Same props
<x-artisanpack-table> Table Different slot API (see below)
<x-artisanpack-tabs> Tabs Props-based API
<x-artisanpack-badge> Badge Same props
<x-artisanpack-avatar> Avatar Same props
<x-artisanpack-loading> Loading Same props
<x-artisanpack-tooltip> Tooltip Same props
<x-artisanpack-drawer> Drawer wire:model becomes v-model
<x-artisanpack-dropdown> Dropdown Same props
<x-artisanpack-menu> Menu Same props
<x-artisanpack-breadcrumbs> Breadcrumbs Same props
<x-artisanpack-pagination> Pagination Same props
<x-artisanpack-steps> Steps Same props
<x-artisanpack-progress> Progress Same props

Common Patterns

Data binding

{{-- Livewire --}}
<x-artisanpack-input wire:model="name" label="Name" />
<!-- Vue -->
<Input v-model="name" label="Name" />

Event handling

{{-- Livewire --}}
<x-artisanpack-button wire:click="save" color="primary">Save</x-artisanpack-button>
<!-- Vue -->
<Button @click="save" color="primary">Save</Button>

Slots

{{-- Livewire --}}
<x-artisanpack-card>
    <x-slot:header>Title</x-slot:header>
    Content
    <x-slot:footer>
        <x-artisanpack-button>Action</x-artisanpack-button>
    </x-slot:footer>
</x-artisanpack-card>
<!-- Vue -->
<Card>
  <template #header>Title</template>
  Content
  <template #footer>
    <Button>Action</Button>
  </template>
</Card>
{{-- Livewire --}}
<x-artisanpack-modal wire:model="showModal" title="Confirm">
    Are you sure?
</x-artisanpack-modal>
<!-- Vue -->
<Modal v-model="showModal" title="Confirm">
  Are you sure?
</Modal>

Table

The Table component uses Vue scoped slots instead of Blade's @scope directive:

{{-- Livewire --}}
<x-artisanpack-table :headers="$headers" :rows="$users">
    @scope('cell_name', $user)
        <strong>{{ $user->name }}</strong>
    @endscope
</x-artisanpack-table>
<!-- Vue -->
<Table :columns="columns" :rows="users">
  <template #cell-name="{ row }">
    <strong>{{ row.name }}</strong>
  </template>
</Table>

Error handling

{{-- Livewire --}}
<x-artisanpack-input
    wire:model="email"
    label="Email"
    :error="$errors->first('email')"
/>
<!-- Vue with Inertia -->
<script setup>
import { InertiaInput } from '@artisanpack-ui/vue-laravel';
import { useInertiaForm } from '@artisanpack-ui/vue-laravel';
const { form, getError, post } = useInertiaForm({ email: '' });
</script>

<template>
  <InertiaInput name="email" v-model="form.email" label="Email" :error="getError('email')" />
</template>

Loading states

{{-- Livewire --}}
<x-artisanpack-button wire:click="save" color="primary">
    <span wire:loading.remove>Save</span>
    <span wire:loading>Saving...</span>
</x-artisanpack-button>
<!-- Vue -->
<Button @click="save" color="primary" :loading="processing">
  Save
</Button>

Step-by-Step Migration

  1. Install Vue packages: npm install @artisanpack-ui/vue @artisanpack-ui/vue-laravel
  2. Set up Inertia.js in your Laravel app if not already done
  3. Register plugins in your app.ts (see Laravel Inertia Integration)
  4. Convert pages one at a time: Start with simpler pages (static content, basic forms) before tackling complex interactive pages
  5. Replace Blade components with Vue imports using the mapping table above
  6. Replace wire:model with v-model
  7. Replace wire:click with @click and move handler logic to <script setup>
  8. Replace Blade slots with Vue template slots
  9. Replace server-side validation display with useInertiaForm() + getError()
  10. Test each page after conversion

Props That Changed

Most props are identical between Livewire and Vue versions. Notable differences:

Prop Livewire Vue Notes
wire:model Livewire binding v-model Standard Vue binding
error $errors->first('field') getError('field') Via useInertiaForm()
icon (prefix) o-icon-name Slot-based Use #prefix / #suffix slots
Table cell scopes @scope('cell_name', $row) #cell-name="{ row }" Vue scoped slots

What's Not Available (Yet)

Some Livewire-specific features do not have direct Vue equivalents:

  • Real-time validation via wire:model.live: Use @input with debounce or watchers
  • Deferred loading via wire:init: Use Vue's onMounted lifecycle hook
  • Polling via wire:poll: Use setInterval in onMounted/onUnmounted