Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Armani's avatar
Level 17

Using vue3-toastify in Inertia app

I have an Inertia, Vue App and want to use vue3-toastify as component and use it in main layout like this:

<Toast :type="$page.props.flash.type" :message="$page.props.flash.message"/>

and this is the component I created:

<script setup>
import 'vue3-toastify/dist/index.css';
import {toast} from "vue3-toastify";

const props = defineProps(['type', 'message'])

const options = {
    'position': toast.POSITION.TOP_RIGHT,
    'autoclose': 2000,
    'type': props.type
}


toast(props.message, options)

</script>

but no toast will show in the page.

0 likes
13 replies
Niush's avatar

The current toast(...) line only gets evaluated on first mount. But, what you need is to keep looking for changes in props and re-trigger the toast. Like this:

watch(
  props,
  () => {
    const options = {
      position: toast.POSITION.TOP_RIGHT,
      autoclose: 2000,
      type: props.type,
    };

    toast(props.message, options);
  },
  { immediate: true }
);
Armani's avatar
Level 17

@Niush It is not working because props are not changing, every time I update a record it gives me the same props.

Niush's avatar

@Armani The props is not changing in the Toast component? Or you mean, in the backend response?

Have you tried doing like this in your layout?

<script setup>
import { computed } from 'vue';
import { usePage } from '@inertiajs/vue3';

const page = usePage();
const flash = computed(() => page.props.flash);
</script>

<template>
    <Toast :type="flash.type" :message="flash.message"/>
</template>
Armani's avatar
Level 17

@Niush In the backend the response won't change so watcher not working as expected. I used computed code you provided but still not working. By the way I use Persistent Layout.

Niush's avatar

@Armani How are you setting flash type and message in backend? Have you checked the Network Tab in DevTools to see what the response body contains?

Armani's avatar
Level 17

@Niush Like this:

return back()->with(['message' => env('STORE'), 'type' => 'success']);

and in handleInertiaRequest.php like this:

'flash' => [
                'message' => fn() => $request->session()->get('message'),
                'type' => fn() => $request->session()->get('type')
  ]
Armani's avatar
Level 17

@Niush I added current date and time then it works:

<Toast :type="flash.type" :message="flash.message" :time="new Date()" />

but I don't like it like this.

Niush's avatar

@Armani Oh, now I see. You meant the type and message content are same and Vue does not know it's changed. There might be better ways but something like this can also be done:

'flash' => [
    'message' => fn() => $request->session()->get('message'),
    'type' => fn() => $request->session()->get('type'),
    'id' => fn() => now()->timestamp
]
<Toast :type="flash.type" :message="flash.message" :key="flash.id" v-if="flash.message" />
1 like
danielcoimbra's avatar

@armani

I implemented a working Toast solution using a similar approach for the Modular Project.

You can find the Toast Component and the related "watch" method for the Laravel's Flash Session here.

Hope it helps.

1 like
Armani's avatar
Armani
OP
Best Answer
Level 17

The answer is to use inertia event like this:

const page = usePage()

let finish = router.on('finish', () => {
    if (page.props.flash.message) {
        toast[page.props.flash.type](page.props.flash.message, {position: toast.POSITION.TOP_CENTER,})
    }
})

onUnmounted(() => finish())
2 likes
bambamboole's avatar

@armani works like a charm also with vue-toastification.

I have on the backend a slightly different usage. It is an object which can contain error, success, warning and info.

Middleware:

class HandleInertiaRequests extends Middleware
{
    public function share(Request $request): array
    {
        return [
            ...parent::share($request),
            'auth' => [
                'user' => $request->user(),
            ],
            'flash' => fn() => collect(['success', 'error', 'warning', 'info'])
                ->mapWithKeys(fn ($key) => [$key => $request->session()->pull($key)])
                ->filter()
                ->all(),
        ];
    }
}

Toast vue component (Maybe this can be handled smarter)

<script setup lang="ts">
import { onUnmounted } from 'vue';
import { usePage, router } from '@inertiajs/vue3';
import { useToast, TYPE } from 'vue-toastification';
const toast = useToast();

const page = usePage();

let finish = router.on('finish', () => {
    for (const [key, value] of Object.entries(page.props.flash)) {
        if (value === null) {
            continue;
        }
        toast(value, {
            type: key as TYPE,
        });
    }
});

onUnmounted(() => finish());
</script>

<template></template>
2 likes

Please or to participate in this conversation.