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

lemmon's avatar
Level 28

Toast comes back if you navigate away and return back to the page

Hello all, In the Build a Forum with Laravel by @lukedowning19 , in lesson video 22 Useful User Feedbacks: Toasts. I noticed that if you create a comment or a post or delete a post or whatever and you use the back arrow to got backward and then you move forward again to the page where you had created or deleted or updated that the banner/toast shows up again and you can do this over and over again. is this fixable or is this just the Vite?

0 likes
9 replies
LaryAI's avatar
Level 58

The behavior you're describing is likely due to the way the browser handles the history state and caching of the page. When you navigate using the back and forward buttons, the browser may serve a cached version of the page, including the state of the DOM at the time you left it, which includes the toast message being visible.

To prevent the toast from reappearing when navigating back and forth, you can use JavaScript to detect when the page is loaded from the cache and then hide or remove the toast message. Here's a solution using vanilla JavaScript that you can include in your page:

window.addEventListener('load', function() {
    // Check if the page was restored from the cache
    if (performance.navigation.type === performance.navigation.TYPE_BACK_FORWARD) {
        // Your code to hide or remove the toast message
        const toast = document.querySelector('.toast-selector'); // Replace with your toast's selector
        if (toast) {
            toast.style.display = 'none'; // Or any other method to hide or remove the toast
        }
    }
});

Alternatively, if you're using a front-end framework like Vue or React, you can manage the visibility of the toast message with a state variable that is reset when the component mounts.

For Vue 3, it might look something like this:

<template>
  <div v-if="showToast" class="toast">
    <!-- Toast content -->
  </div>
</template>

<script>
export default {
  data() {
    return {
      showToast: true
    };
  },
  mounted() {
    if (performance.navigation.type === performance.navigation.TYPE_BACK_FORWARD) {
      this.showToast = false;
    }
  }
};
</script>

For React, you could use the useEffect hook to detect when the component mounts:

import React, { useState, useEffect } from 'react';

const ToastComponent = () => {
  const [showToast, setShowToast] = useState(true);

  useEffect(() => {
    if (window.performance && window.performance.navigation.type === window.performance.navigation.TYPE_BACK_FORWARD) {
      setShowToast(false);
    }
  }, []);

  return (
    <>
      {showToast && (
        <div className="toast">
          {/* Toast content */}
        </div>
      )}
    </>
  );
};

export default ToastComponent;

Please note that performance.navigation is deprecated and you should use the PerformanceNavigationTiming interface instead. However, for simplicity and broad compatibility, the above examples use performance.navigation.

Remember to replace .toast-selector with the actual selector for your toast message, and adjust the logic to fit the framework or library you're using.

lemmon's avatar
Level 28

maybe we need to clear the flash message somehow since show is always true?

<script setup>
import {onUnmounted, ref, watchEffect} from 'vue';
import { usePage } from '@inertiajs/vue3';

const page = usePage();
const show = ref(true);
const style = ref('success');
const message = ref('');
const timeout = ref(null);

watchEffect(async () => {
    style.value = page.props.jetstream.flash?.bannerStyle || 'success';
    message.value = page.props.jetstream.flash?.banner || '';
    show.value = true;

    clearTimeout(timeout.value);
    timeout.value = setTimeout(() => show.value = false, 5000);
});

onUnmounted(() => clearTimeout(timeout.value));
</script>

<template>
    <div class="sticky top-0 z-40">
        <div v-if="show && message" :class="{ 'bg-indigo-500': style == 'success', 'bg-red-700': style == 'danger' }">
            <div class="max-w-screen-xl mx-auto py-2 px-3 sm:px-6 lg:px-8">
                <div class="flex items-center justify-between flex-wrap">
                    <div class="w-0 flex-1 flex items-center min-w-0">
                        <span class="flex p-2 rounded-lg" :class="{ 'bg-indigo-600': style == 'success', 'bg-red-600': style == 'danger' }">
                            <svg v-if="style == 'success'" class="h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                                <path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
                            </svg>

                            <svg v-if="style == 'danger'" class="h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                                <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
                            </svg>
                        </span>

                        <p class="ms-3 font-medium text-sm text-white truncate">
                            {{ message }}
                        </p>
                    </div>

                    <div class="shrink-0 sm:ms-3">
                        <button
                            type="button"
                            class="-me-1 flex p-2 rounded-md focus:outline-none sm:-me-2 transition"
                            :class="{ 'hover:bg-indigo-600 focus:bg-indigo-600': style == 'success', 'hover:bg-red-600 focus:bg-red-600': style == 'danger' }"
                            aria-label="Dismiss"
                            @click.prevent="show = false"
                        >
                            <svg class="h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                                <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
                            </svg>
                        </button>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>
lemmon's avatar
Level 28

@lukedowning19 is that episode past episode 28 because I already finished episode 28 and we haven't fixed it yet so that means it's probably past episode 28 right thank you

lemmon's avatar
Level 28

@lukedowning19 thank you very much I was not sold on inertia, but this tutorial has open my eyes thank you very much. Very good tutorial.

1 like

Please or to participate in this conversation.