Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
Add Docs
MingleJS
https://github.com/ijpatricio/mingle
Admin
MingleJS is a Laravel Livewire library that enables React and Vue components to be used within
...
Tokens:
4,847
Snippets:
42
Trust Score:
9.2
Update:
3 weeks ago
Context
Skills
Chat
Benchmark
89.9
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# MingleJS MingleJS is a Laravel package that enables React and Vue components to be seamlessly integrated into Livewire applications. It creates "islands of interactivity" by rendering a div container server-side, then mounting the JavaScript component client-side, allowing developers to leverage the reactivity of modern JavaScript frameworks within a Livewire-driven application. The package provides a bridge between Livewire's server-side rendering capabilities and React/Vue's client-side interactivity. Components can receive initial data from PHP via `mingleData()`, and the JavaScript components gain access to a `wire` object that enables direct server method calls using Livewire's wire protocol, eliminating the need for separate AJAX calls with Axios or Fetch. ## Installation ### Install MingleJS via Composer Install the package and run the installer command to configure your Laravel application for MingleJS. ```bash # Install the package composer require ijpatricio/mingle # Run the installer to set up Vite config and layout files php artisan mingle:install # Optional: Install with demo component php artisan mingle:install --with-demo ``` ## Creating Mingle Components ### make:mingle Artisan Command The `make:mingle` command scaffolds both the Livewire PHP component and the corresponding JavaScript files for your chosen framework (React or Vue). ```bash # Create a React mingle component php artisan make:mingle react TodoList # Create a Vue mingle component php artisan make:mingle vue UserProfile # Specify a custom JavaScript file path php artisan make:mingle react Dashboard --jsfile=resources/js/dashboard/Dashboard.js # Force overwrite existing files php artisan make:mingle vue Settings --force ``` ## Livewire Component Setup ### HasMingles Interface and InteractsWithMingles Trait Create a Livewire component that implements `HasMingles` interface and uses the `InteractsWithMingles` trait. Define the JavaScript component path in `component()` and pass data to the frontend via `mingleData()`. ```php <?php namespace App\Livewire; use Ijpatricio\Mingle\Concerns\InteractsWithMingles; use Ijpatricio\Mingle\Contracts\HasMingles; use Livewire\Component; class TodoList extends Component implements HasMingles { use InteractsWithMingles; public function component(): string { return 'resources/js/todo-list/index.js'; } public function mingleData(): array { return [ 'initialTodos' => [ ['id' => 1, 'title' => 'Learn MingleJS', 'completed' => false], ['id' => 2, 'title' => 'Build awesome apps', 'completed' => false], ], 'user' => auth()->user()->name, ]; } // Server-side methods callable from JavaScript via wire public function addTodo(string $title): array { $todo = [ 'id' => rand(1000, 9999), 'title' => $title, 'completed' => false, ]; // Save to database here... return $todo; } public function toggleTodo(int $id): bool { // Toggle in database and return new state return true; } public function deleteTodo(int $id): void { // Delete from database } } ``` ## React Component Integration ### Registering React Components with mingleReact Create a JavaScript entry file that registers your React component with MingleJS. The component receives `wire` for server communication and `mingleData` containing the initial data from PHP. ```javascript // resources/js/todo-list/index.js import mingle from '@mingle/mingleReact.jsx' import TodoList from './TodoList.jsx' mingle('resources/js/todo-list/index.js', TodoList) ``` ```jsx // resources/js/todo-list/TodoList.jsx import React, { useState } from 'react' function TodoList({ wire, mingleData }) { const [todos, setTodos] = useState(mingleData.initialTodos) const [newTodo, setNewTodo] = useState('') const [loading, setLoading] = useState(false) const addTodo = async () => { if (!newTodo.trim()) return setLoading(true) try { // Call PHP method via Livewire wire const todo = await wire.addTodo(newTodo) setTodos([...todos, todo]) setNewTodo('') } catch (error) { console.error('Failed to add todo:', error) } finally { setLoading(false) } } const toggleTodo = async (id) => { const completed = await wire.toggleTodo(id) setTodos(todos.map(todo => todo.id === id ? { ...todo, completed } : todo )) } const deleteTodo = async (id) => { await wire.deleteTodo(id) setTodos(todos.filter(todo => todo.id !== id)) } return ( <div className="p-6 bg-white rounded-lg shadow"> <h2 className="text-xl font-bold mb-4"> Welcome, {mingleData.user}! </h2> <div className="flex gap-2 mb-4"> <input type="text" value={newTodo} onChange={(e) => setNewTodo(e.target.value)} placeholder="Add a new todo..." className="flex-1 px-3 py-2 border rounded" /> <button onClick={addTodo} disabled={loading} className="px-4 py-2 bg-blue-500 text-white rounded" > {loading ? 'Adding...' : 'Add'} </button> </div> <ul className="space-y-2"> {todos.map(todo => ( <li key={todo.id} className="flex items-center gap-2"> <input type="checkbox" checked={todo.completed} onChange={() => toggleTodo(todo.id)} /> <span className={todo.completed ? 'line-through' : ''}> {todo.title} </span> <button onClick={() => deleteTodo(todo.id)} className="text-red-500" > Delete </button> </li> ))} </ul> </div> ) } export default TodoList ``` ## Vue Component Integration ### Registering Vue Components with mingleVue Register Vue components similarly to React. The component receives `wire` and `mingleData` as props. You can also listen to Livewire events dispatched from the server using `wire.on()`. ```javascript // resources/js/counter/index.js import mingle from '@mingle/mingleVue' import Counter from './Counter.vue' mingle('resources/js/counter/index.js', Counter) ``` ```vue <!-- resources/js/counter/Counter.vue --> <script setup> import { ref } from 'vue' const props = defineProps({ wire: {}, mingleData: {}, }) const count = ref(1) const message = ref(props.mingleData.message) const doubleCurrentCount = () => { props.wire.doubleIt(count.value).then((data) => { count.value = data }) } // Listen to Livewire events dispatched from PHP props.wire.on('doubleIt', (randomString) => { console.log('Event received from server:', randomString) }) </script> <template> <div class="p-6 bg-white rounded-lg shadow"> <h2 class="text-lg font-bold">Vue Counter Component</h2> <div class="mt-4"> Initial message: {{ message }} </div> <div class="mt-4 flex gap-4 items-center"> <button @click="count = 1" class="px-4 py-2 bg-gray-200 rounded" > Reset </button> <span>Current Count: {{ count }}</span> <button @click="doubleCurrentCount" class="px-4 py-2 bg-blue-500 text-white rounded" > Double It (Server Call) </button> </div> </div> </template> ``` ## Advanced Vue Integration ### createMingle for Custom Vue App Configuration Use `createMingle` instead of the default `mingle` function when you need to customize the Vue app instance, such as adding plugins, global properties, or Pinia stores. ```javascript // resources/js/advanced-app/index.js import { createMingle } from '@mingle/mingleVue' import App from './App.vue' import { createPinia } from 'pinia' // Custom notification plugin const notificationPlugin = { install: (app, options) => { app.config.globalProperties.$notify = (message, type = 'info') => { console.log(`[${type.toUpperCase()}] ${message}`) // Your notification logic here } } } createMingle('resources/js/advanced-app/index.js', ({ createApp, props, el, wire, mingleId, wireId, mingleData }) => { const app = createApp(App, props) // Add Pinia for state management app.use(createPinia()) // Add custom plugins app.use(notificationPlugin) // Add global properties app.config.globalProperties.$api = { baseUrl: '/api/v1', } // Mount the app app.mount(el) return true }) ``` ## Blade Integration ### @mingles Directive Add the `@mingles` Blade directive to your layout file to include the JavaScript for all registered Mingle components. This should be placed in your main layout, typically in the `<head>` or before the closing `</body>` tag. ```blade <!-- resources/views/layouts/app.blade.php --> <!DOCTYPE html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>{{ config('app.name') }}</title> @vite(['resources/css/app.css', 'resources/js/app.js']) @mingles @livewireStyles </head> <body> {{ $slot }} @livewireScripts </body> </html> ``` ```blade <!-- resources/views/dashboard.blade.php --> <x-app-layout> <div class="py-12"> <div class="max-w-7xl mx-auto sm:px-6 lg:px-8"> <h1 class="text-2xl font-bold mb-6">Dashboard</h1> <!-- Use Mingle components like any Livewire component --> <div class="grid grid-cols-2 gap-6"> <livewire:todo-list /> <livewire:vue-counter /> </div> </div> </div> </x-app-layout> ``` ## Lazy Loading with Placeholders ### Livewire Lazy Attribute MingleJS components support Livewire's lazy loading feature. Add the `#[Lazy]` attribute to defer component loading, and define a `placeholder()` method to show a loading state. ```php <?php namespace App\Livewire; use Ijpatricio\Mingle\Concerns\InteractsWithMingles; use Ijpatricio\Mingle\Contracts\HasMingles; use Livewire\Attributes\Lazy; use Livewire\Component; #[Lazy] class HeavyChart extends Component implements HasMingles { use InteractsWithMingles; public function component(): string { return 'resources/js/chart/index.js'; } public function mount() { // Simulate heavy database query sleep(2); } public function placeholder() { return <<<'HTML' <div class="flex items-center justify-center w-full h-64 bg-gray-100 rounded-lg animate-pulse"> <div class="text-center"> <svg class="w-12 h-12 mx-auto text-gray-400 animate-spin" fill="none" viewBox="0 0 24 24"> <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path> </svg> <p class="mt-2 text-gray-500">Loading chart data...</p> </div> </div> HTML; } public function mingleData(): array { return [ 'chartData' => $this->fetchChartData(), 'options' => ['responsive' => true], ]; } private function fetchChartData(): array { // Heavy database queries here return [ 'labels' => ['Jan', 'Feb', 'Mar', 'Apr', 'May'], 'datasets' => [ ['label' => 'Sales', 'data' => [12, 19, 3, 5, 2]], ], ]; } } ``` ## Configuration ### mingle.php Config File Publish and customize the MingleJS configuration to set the default JavaScript base path and configure React preamble for hot module replacement. ```php <?php // config/mingle.php return [ /* |-------------------------------------------------------------------------- | JavaScript Base Path |-------------------------------------------------------------------------- | | The default directory where Mingle JavaScript components are stored. | This path is relative to your Laravel application root. | */ 'js-basepath' => 'resources/js', /* |-------------------------------------------------------------------------- | React Preamble |-------------------------------------------------------------------------- | | Enable the React refresh preamble for hot module replacement during | development. This should be enabled when using React components. | */ 'react_preamble_enabled' => true, ]; ``` ## Server Events Communication ### Dispatching Events from PHP to JavaScript Use Livewire's dispatch mechanism to send events from PHP to your JavaScript components. Listen for these events in Vue using `wire.on()`. ```php <?php namespace App\Livewire; use Ijpatricio\Mingle\Concerns\InteractsWithMingles; use Ijpatricio\Mingle\Contracts\HasMingles; use Livewire\Component; class NotificationCenter extends Component implements HasMingles { use InteractsWithMingles; public function component(): string { return 'resources/js/notifications/index.js'; } public function mingleData(): array { return [ 'notifications' => [], ]; } public function markAsRead(int $notificationId): void { // Update database... // Dispatch event to JavaScript component $this->dispatch('notification-read', id: $notificationId, timestamp: now()->toISOString() ); } public function clearAll(): void { // Clear all notifications in database... $this->dispatch('notifications-cleared'); } } ``` ```vue <!-- resources/js/notifications/Notifications.vue --> <script setup> import { ref, onMounted } from 'vue' const props = defineProps({ wire: {}, mingleData: {}, }) const notifications = ref(props.mingleData.notifications) // Listen for server-dispatched events props.wire.on('notification-read', ({ id, timestamp }) => { console.log(`Notification ${id} marked as read at ${timestamp}`) notifications.value = notifications.value.filter(n => n.id !== id) }) props.wire.on('notifications-cleared', () => { console.log('All notifications cleared') notifications.value = [] }) const markAsRead = (id) => { props.wire.markAsRead(id) } const clearAll = () => { props.wire.clearAll() } </script> <template> <div class="notification-center"> <button @click="clearAll">Clear All</button> <div v-for="notification in notifications" :key="notification.id"> {{ notification.message }} <button @click="markAsRead(notification.id)">Mark Read</button> </div> </div> </template> ``` ## Summary MingleJS enables Laravel developers to create rich, interactive user interfaces by combining Livewire's server-side simplicity with the powerful component ecosystems of React and Vue. The primary use cases include building complex form interfaces with real-time validation, creating data visualization dashboards with charting libraries, implementing real-time collaborative features, and integrating third-party JavaScript widgets that require React or Vue. The `wire` object provides seamless communication between frontend and backend, allowing JavaScript components to call PHP methods directly and receive responses as promises. Integration follows a consistent pattern: create a Livewire component implementing `HasMingles`, define the JavaScript component path and initial data, then register the JavaScript component using `mingle()` or `createMingle()`. The architecture supports lazy loading for performance optimization, Livewire events for server-to-client communication, and full access to the Vue/React ecosystems including state management libraries like Pinia or Redux. This approach lets teams leverage existing JavaScript component libraries while maintaining the developer experience benefits of Livewire's wire protocol.