Set up Bridge UI in a Laravel app with Inertia.js (Vue or React).
Bridge UI integrates cleanly with Laravel and Inertia.js.
Use the framework selector in the site header to switch between React and Vue on framework-specific steps below.
Install packages
Add Bridge UI and icons to your Laravel project. Packages are published on npm as 0.0.3.
bun add @bridge-ui/[email protected]bun add @bridge-ui/[email protected]pnpm add @bridge-ui/[email protected]pnpm add @bridge-ui/[email protected]yarn add @bridge-ui/[email protected]yarn add @bridge-ui/[email protected]npm install @bridge-ui/[email protected]npm install @bridge-ui/[email protected]Vite entry points
Point the Laravel Vite plugin at your Inertia entry:
laravel({
input: ["resources/css/app.css", "resources/js/vue/app.ts"],
}),
Use a Blade root (for example app-vue.blade.php) that loads this Vite entry.
laravel({
input: ["resources/css/app.css", "resources/js/react/app.tsx"],
}),
Use a Blade root (for example app-react.blade.php) that loads this Vite entry.
App entry
Register the Bridge UI plugin in resources/js/vue/app.ts:
import { createBridgeUI } from "@bridge-ui/vue";
import { createInertiaApp } from "@inertiajs/vue3";
import { createApp, h } from "vue";
createInertiaApp({
setup({ el, App, props, plugin }) {
createApp({ render: () => h(App, props) })
.use(plugin)
.use(createBridgeUI())
.mount(el);
},
});
resources/js/react/app.tsx follows the standard Inertia React setup. Bridge UI is provided from the layout—no extra plugin in the entry file:
import { createInertiaApp } from "@inertiajs/react";
import { createRoot } from "react-dom/client";
createInertiaApp({
setup({ el, App, props }) {
createRoot(el).render(<App {...props} />);
},
});
Layout
Wrap your Inertia layout with BridgeUIProvider and BridgeUIHosts so imperative actions (useModalAction, useDialogAction, useSnackbarAction) work across pages.
<script setup lang="ts">
import { BridgeUIProvider } from "@bridge-ui/vue";
import { BridgeUIHosts } from "@bridge-ui/vue/Actions";
</script>
<template>
<BridgeUIProvider :global="{}" :components="{}">
<BridgeUIHosts
:modal="{ modal: { transition: 'fade' } }"
:dialog="{ modal: { transition: 'fade' } }"
:snackbar="{ max: 3, timeout: 5000, position: 'bottom-center' }"
>
<slot />
</BridgeUIHosts>
</BridgeUIProvider>
</template>
import { BridgeUIProvider } from "@bridge-ui/react";
import { BridgeUIHosts } from "@bridge-ui/react/Actions";
import type { PropsWithChildren } from "react";
const Layout = ({ children }: PropsWithChildren) => (
<BridgeUIProvider global={{}} components={{}}>
<BridgeUIHosts
modal={{ modal: { transition: "fade" } }}
dialog={{ modal: { transition: "fade" } }}
snackbar={{ max: 3, timeout: 5000, position: "bottom-center" }}
>
{children}
</BridgeUIHosts>
</BridgeUIProvider>
);
export default Layout;
Select async data
The Select component accepts an asyncData object with search and resolve callbacks. In Laravel apps, these typically call JSON API routes via axios.
Below is a complete pattern you can copy: a JSON route, a controller, and a small client helper.
Laravel route
// routes/web.php
use App\Http\Controllers\Examples\UserSelectController;
Route::get('/api/examples/users', UserSelectController::class);
Controller
The controller returns { label, value, description } objects compatible with Select options:
<?php
namespace App\Http\Controllers\Examples;
use App\Models\User;
use Illuminate\Http\Request;
class UserSelectController extends Controller
{
public function __invoke(Request $request)
{
$ids = $request->collect('ids')->filter()->values();
return User::query()
->select('id', 'name', 'email')
->when($ids->isNotEmpty(), function ($query) use ($ids) {
$query->whereIn('id', $ids);
})
->when($ids->isEmpty() && $request->filled('search'), function ($query) use ($request) {
$search = $request->string('search');
$query->where('name', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
})
->orderBy('name')
->limit(20)
->get()
->map(fn (User $user) => [
'value' => $user->id,
'label' => $user->name,
'description' => $user->email,
]);
}
}
Query parameters:
| Param | Purpose |
|---|---|
| search | Filter options while the user types |
| ids | Resolve labels for already-selected values |
Client helper
Create resources/js/lib/laravelSelect.ts:
import type { SelectAsyncData, SelectValue } from "@bridge-ui/core";
import axios from "axios";
export function laravelSelect(options: { url: string }): SelectAsyncData {
const { url } = options;
return {
limit: 20,
resolve: async (values: SelectValue[]) => {
const { data } = await axios.get(url, {
params: { ids: values },
headers: { Accept: "application/json" },
});
return data;
},
search: async (query) => {
const params: Record<string, string> = {};
if (query) params.search = query;
const { data } = await axios.get(url, {
params,
headers: { Accept: "application/json" },
});
return data;
},
};
}
Usage in a page
<script setup lang="ts">
import { laravelSelect } from "@/examples/core/integrations/laravel/laravelSelect";
import { Select } from "@bridge-ui/vue/Components/Select";
import { ref } from "vue";
const userId = ref(null);
</script>
<template>
<Select
label="User"
v-model="userId"
placeholder="Type to search users..."
:async-data="laravelSelect({ url: '/api/examples/users' })"
/>
</template>
import { useState } from "react";
import { laravelSelect } from "@/examples/core/integrations/laravel/laravelSelect";
import { Select } from "@bridge-ui/react/Components/Select";
export default function UserSelect() {
const [userId, setUserId] = useState<null | number | string>(null);
return (
<Select
label="User"
value={userId}
onChange={setUserId}
placeholder="Type to search users..."
asyncData={laravelSelect({ url: "/api/examples/users" })}
/>
);
}
asyncData implies searchable. Selected values are resolved through the same route with ids when the page loads or when chips need labels.
For SPAs without a backend, pass inline callbacks instead—see the Async data playground on the Select page.
Next steps
- useModalAction — imperative modals from Inertia pages
- useDialogAction — confirm-style dialogs
- useSnackbarAction — toast notifications