PHPFlasher works seamlessly with Inertia.js, providing a smooth way to display flash notifications in your single-page applications.
PHP Version
Laravel Version
Inertia.js
Using older versions?
If you need to use PHP < v8.2, Laravel < v11.0, or an older Inertia.js version, use PHPFlasher v1 instead. Check out the v1 documentation here .
Installation
To use PHPFlasher with Inertia.js, you'll need to install both the PHP package and JavaScript package.
1. Install the PHP Package
composer require php-flasher/flasher-laravel
2. Add JavaScript Package
Add @flasher/flasher
to your package.json
:
{
"dependencies": {
"@flasher/flasher": "file:vendor/php-flasher/flasher/Resources"
}
}
Then, install the JavaScript dependencies:
npm install --force
Why the --force flag?
The --force
flag is required because we're installing a local package from the filesystem
rather than from npm. This is the recommended approach for PHPFlasher to ensure version compatibility.
Usage
To use PHPFlasher with Inertia.js, you need to:
1. Modify Your Middleware
Update your HandleInertiaRequests
middleware to share flash notifications:
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Inertia\Middleware;
class HandleInertiaRequests extends Middleware
{
/**
* Defines the props that are shared by default.
*
* @see https://inertiajs.com/shared-data
*/
public function share(Request $request): array
{
return array_merge(parent::share($request), [
// Add flash messages to Inertia shared data
'messages' => flash()->render('array'),
// You can include other shared data here
'auth' => [
'user' => $request->user(),
],
]);
}
}
Pro Tip: Multiple Ways to Share Flash Messages
The render('array')
method
converts flash messages to a format compatible with JavaScript. You can also use render('json')
and
parse it in your frontend code.
2. Set Up Your Frontend
Add the code to render notifications in your layout component (example shown with Vue.js):
<script>
import flasher from "@flasher/flasher";
export default {
props: {
messages: Object,
},
watch: {
messages(value) {
if (value) {
flasher.render(value);
}
}
},
mounted() {
// Display messages on initial load
if (this.messages) {
flasher.render(this.messages);
}
}
}
</script>
<template>
<div>
<header class="bg-white shadow">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
<!-- Your navigation -->
<h1 class="text-xl font-bold text-gray-900">My App</h1>
</div>
</header>
<main>
<div class="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
<slot />
</div>
</main>
<footer class="bg-white border-t border-gray-200 mt-auto">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
<p class="text-center text-gray-500 text-sm">
© 2025 My Application
</p>
</div>
</footer>
</div>
</template>
3. Creating Flash Messages
Now you can create flash notifications in your controllers:
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Redirect;
use Inertia\Inertia;
class UsersController extends Controller
{
/**
* Display a listing of users.
*/
public function index()
{
return Inertia::render('Users/Index', [
'users' => User::all()
]);
}
/**
* Show the form for creating a new user.
*/
public function create()
{
return Inertia::render('Users/Create');
}
/**
* Store a newly created user in storage.
*/
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:8|confirmed',
]);
User::create([
'name' => $validated['name'],
'email' => $validated['email'],
'password' => Hash::make($validated['password']),
]);
// Add a success notification
flash()->success('User created successfully!');
// Redirect back, and Inertia will handle passing the flash message
return Redirect::route('users.index');
}
/**
* Display the specified user.
*/
public function show(User $user)
{
return Inertia::render('Users/Show', [
'user' => $user
]);
}
/**
* Show the form for editing the specified user.
*/
public function edit(User $user)
{
return Inertia::render('Users/Edit', [
'user' => $user
]);
}
/**
* Update the specified user in storage.
*/
public function update(Request $request, User $user)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users,email,' . $user->id,
]);
$user->update($validated);
flash()->success('User updated successfully!');
return Redirect::route('users.index');
}
/**
* Remove the specified user from storage.
*/
public function destroy(User $user)
{
$user->delete();
flash()->info('User has been deleted.');
return Redirect::route('users.index');
}
}
Success Message
flash()->success('Item created successfully!')
Error Message
flash()->error('An error occurred!')
Warning Message
flash()->warning('Please review your submission.')
Info Message
flash()->info('Your session expires in 10 minutes.')
Expert Tips for Inertia.js Integration
- Keep It Consistent: Use the same notification types for similar actions throughout your application for a predictable user experience.
- Form Validation: Use flash notifications for general form status, but rely on Inertia's built-in error handling for field-specific validation errors.
-
Watch for Changes: Always use the
watch
property to detect new notifications after navigation or form submissions. - Package Bundling: When using Vite or Webpack, ensure @flasher/flasher is properly included in your bundle.
Examples
CRUD Operations
Here's a complete example of CRUD operations with flash messages:
<?php
namespace App\Http\Controllers;
use App\Models\Product;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
use Inertia\Inertia;
class ProductController extends Controller
{
/**
* Display a listing of products.
*/
public function index()
{
return Inertia::render('Products/Index', [
'products' => Product::all()
]);
}
/**
* Show the form for creating a new product.
*/
public function create()
{
return Inertia::render('Products/Create');
}
/**
* Store a newly created product in storage.
*/
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'price' => 'required|numeric',
'description' => 'nullable|string|max:1000',
'category_id' => 'required|exists:categories,id',
]);
Product::create($validated);
flash()->success('Product created successfully!');
return Redirect::route('products.index');
}
/**
* Display the specified product.
*/
public function show(Product $product)
{
return Inertia::render('Products/Show', [
'product' => $product->load('category')
]);
}
/**
* Show the form for editing the specified product.
*/
public function edit(Product $product)
{
return Inertia::render('Products/Edit', [
'product' => $product,
'categories' => \App\Models\Category::all()
]);
}
/**
* Update the specified product in storage.
*/
public function update(Request $request, Product $product)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'price' => 'required|numeric',
'description' => 'nullable|string|max:1000',
'category_id' => 'required|exists:categories,id',
]);
$product->update($validated);
flash()->success('Product updated successfully!');
return Redirect::route('products.index');
}
/**
* Remove the specified product from storage.
*/
public function destroy(Product $product)
{
try {
$product->delete();
flash()->info('Product has been deleted.');
} catch (\Exception $e) {
flash()->error('Cannot delete this product. It may be in use.');
}
return Redirect::route('products.index');
}
}
Best Practice: Error Handling
Always wrap database operations in try-catch blocks and provide user-friendly error messages. This helps users understand what went wrong and what actions they can take to resolve issues.
Authentication Flow
Here's an example of using flash notifications in an authentication flow:
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Validation\ValidationException;
use Inertia\Inertia;
class AuthController extends Controller
{
/**
* Show the login form
*/
public function showLogin()
{
return Inertia::render('Auth/Login');
}
/**
* Handle user login request
*/
public function login(Request $request)
{
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required',
]);
if (Auth::attempt($credentials, $request->boolean('remember'))) {
$request->session()->regenerate();
flash()->success('Welcome back, ' . Auth::user()->name . '!');
return Redirect::intended(route('dashboard'));
}
throw ValidationException::withMessages([
'email' => __('auth.failed'),
]);
}
/**
* Show registration form
*/
public function showRegistration()
{
return Inertia::render('Auth/Register');
}
/**
* Handle user registration
*/
public function register(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:8|confirmed',
]);
$user = User::create([
'name' => $validated['name'],
'email' => $validated['email'],
'password' => Hash::make($validated['password']),
]);
Auth::login($user);
flash()->success('Welcome to our application! Your account has been created.');
return Redirect::route('dashboard');
}
/**
* Log the user out
*/
public function logout(Request $request)
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
flash()->info('You have been logged out successfully.');
return Redirect::route('login');
}
}
Form Validation
Combine PHPFlasher with Inertia's validation error handling:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
use Inertia\Inertia;
class FormController extends Controller
{
/**
* Show the contact form
*/
public function showContactForm()
{
return Inertia::render('Contact/Form');
}
/**
* Handle the contact form submission
*/
public function submitContactForm(Request $request)
{
try {
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email',
'message' => 'required|string|min:10|max:1000',
]);
// Process the form data
// e.g., send an email, save to database, etc.
// Show success notification
flash()->success(
'Thank you for contacting us! We will get back to you soon.',
'Message Sent'
);
return Redirect::route('contact.thankyou');
} catch (\Illuminate\Validation\ValidationException $e) {
// With Inertia, validation errors are automatically passed back
// Add a generic error message as well
flash()->error('Please fix the errors in your form.', 'Form Validation Error');
// With Inertia, this will redirect back with errors
throw $e;
} catch (\Exception $e) {
// Handle other exceptions
flash()->error(
'Sorry, we could not process your request. Please try again later.',
'System Error'
);
return Redirect::back();
}
}
/**
* Show thank you page
*/
public function showThankYou()
{
return Inertia::render('Contact/ThankYou');
}
}
Corresponding Vue Form Component
<script setup>
import { useForm } from '@inertiajs/vue3'
import Layout from '@/Layouts/MainLayout.vue'
const form = useForm({
name: '',
email: '',
message: ''
})
const submit = () => {
form.post(route('contact.submit'))
}
</script>
<template>
<Layout>
<div class="max-w-2xl mx-auto p-4 sm:p-6 lg:p-8">
<h1 class="text-2xl font-semibold text-gray-900 mb-6">Contact Us</h1>
<form @submit.prevent="submit" class="space-y-6">
<div>
<label for="name" class="block text-sm font-medium text-gray-700">Name</label>
<input
id="name"
v-model="form.name"
type="text"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-emerald-500 focus:ring focus:ring-emerald-200 focus:ring-opacity-50"
/>
<div v-if="form.errors.name" class="text-red-500 text-sm mt-1"></div>
</div>
<div>
<label for="email" class="block text-sm font-medium text-gray-700">Email</label>
<input
id="email"
v-model="form.email"
type="email"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-emerald-500 focus:ring focus:ring-emerald-200 focus:ring-opacity-50"
/>
<div v-if="form.errors.email" class="text-red-500 text-sm mt-1"></div>
</div>
<div>
<label for="message" class="block text-sm font-medium text-gray-700">Message</label>
<textarea
id="message"
v-model="form.message"
rows="4"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-emerald-500 focus:ring focus:ring-emerald-200 focus:ring-opacity-50"
></textarea>
<div v-if="form.errors.message" class="text-red-500 text-sm mt-1"></div>
</div>
<div class="flex justify-end">
<button
type="submit"
:disabled="form.processing"
class="inline-flex items-center px-4 py-2 bg-emerald-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-emerald-700 active:bg-emerald-800 focus:outline-none focus:border-emerald-800 focus:ring focus:ring-emerald-300 disabled:opacity-25 transition"
>
<span v-if="form.processing">Processing...</span>
<span v-else>Submit</span>
</button>
</div>
</form>
</div>
</Layout>
</template>
Tip: Handling Both Generic and Field-Specific Errors
As shown in the example above, it's often helpful to use PHPFlasher for global form status messages while using Inertia's built-in error handling for field-specific validation errors. This gives users both context-specific and general feedback.
Frontend Framework Integration
Inertia.js works with multiple frontend frameworks. Here's how to integrate PHPFlasher with each:
Vue.js Integration
<script setup>
import { computed, watch } from 'vue'
import { usePage } from '@inertiajs/vue3'
import flasher from '@flasher/flasher'
// Access shared data from Inertia
const page = usePage()
const messages = computed(() => page.props.messages)
// Watch for changes in flash messages
watch(
messages,
(newMessages) => {
if (newMessages) {
flasher.render(newMessages)
}
},
{ immediate: true }
)
</script>
<template>
<div class="min-h-screen bg-gray-100">
<nav class="bg-white border-b border-gray-100">
<!-- Navigation content -->
</nav>
<!-- Page Content -->
<main>
<slot />
</main>
<footer class="bg-white border-t border-gray-100 mt-auto">
<!-- Footer content -->
</footer>
</div>
</template>
Using flash messages in a Vue component:
<script setup>
import { useForm } from '@inertiajs/vue3'
import AppLayout from '@/Layouts/AppLayout.vue'
import flasher from '@flasher/flasher'
const form = useForm({
title: '',
content: ''
})
const submit = () => {
form.post(route('posts.store'), {
onSuccess: () => {
// You can also trigger notifications directly from the frontend
flasher.success('Post created from the frontend!')
// Reset the form
form.reset()
}
})
}
</script>
<template>
<AppLayout>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200">
<form @submit.prevent="submit">
<!-- Form fields -->
<button type="submit" :disabled="form.processing">Create Post</button>
</form>
</div>
</div>
</div>
</div>
</AppLayout>
</template>
Framework-Specific Integration Tips
Vue.js
Use the watch
function with immediate: true
to ensure flash messages are shown on both initial load and after navigation.
React
With React, use useEffect
with messages
as a dependency to catch updates to flash messages.
Svelte
Combine onMount
and afterUpdate
lifecycle functions to ensure flash messages are shown both initially and after updates.
Advanced
Custom Notification Options
Customize your notifications with various options:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Inertia\Inertia;
class NotificationController extends Controller
{
public function showExamples()
{
return Inertia::render('Notifications/Examples');
}
public function positionExample()
{
// Position options
flash()
->option('position', 'bottom-right')
->success('Positioned at the bottom-right');
return back();
}
public function timeoutExample()
{
// Timeout
flash()
->option('timeout', 8000) // 8 seconds
->info('This message stays longer');
return back();
}
public function animationExample()
{
// Animation
flash()
->option('showAnimation', 'fadeIn')
->option('hideAnimation', 'fadeOut')
->success('Custom animations');
return back();
}
public function multipleOptionsExample()
{
// Multiple options at once
flash()
->options([
'position' => 'top-center',
'timeout' => 3000,
'closeButton' => false
])
->success('Multiple options at once');
return back();
}
}
Additional Features
Multiple Themes
Choose from 6+ themes including Toastr, SweetAlert, Notyf, Noty and more.
View themesJavaScript API
Access the full JavaScript API for creating notifications directly from the frontend.
Learn moreReady to enhance your Inertia.js app?
Start using PHPFlasher today and give your users beautiful notifications in minutes!