# 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
[
['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 (
Welcome, {mingleData.user}!
setNewTodo(e.target.value)}
placeholder="Add a new todo..."
className="flex-1 px-3 py-2 border rounded"
/>
{todos.map(todo => (
toggleTodo(todo.id)}
/>
{todo.title}
))}
)
}
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
Vue Counter Component
Initial message: {{ message }}
Current Count: {{ count }}
```
## 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 `` or before the closing `` tag.
```blade
{{ config('app.name') }}
@vite(['resources/css/app.css', 'resources/js/app.js'])
@mingles
@livewireStyles
{{ $slot }}
@livewireScripts
```
```blade
Dashboard
```
## 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
Loading chart data...
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
'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
[],
];
}
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
{{ notification.message }}
```
## 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.