I suggest trying redirecting back instead of fixed routes.
return redirect()->back();
Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.
Hi!
I have this weird issue: Inertia constantly redirects to / after a failed validation.
If a form validation fails, Inertia returns a response with the parameter url as "/".
However, there is one component where it doesn't happen. "Pages/Dashboard"
Any other component has that issue.
See a comparison here (first is ok, second redirects to /):
<script setup>
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue';
import {Head, useForm} from '@inertiajs/vue3';
import PageHeader from "@/Components/PageHeader.vue";
import PrimaryButton from "@/Components/PrimaryButton.vue";
import { ref } from 'vue';
import SecondaryButton from "@/Components/SecondaryButton.vue";
import Modal from "@/Components/Modal.vue";
import TextInput from "@/Components/TextInput.vue";
import InputLabel from "@/Components/InputLabel.vue";
import InputError from "@/Components/InputError.vue";
import {Link} from "@inertiajs/vue3";
import LinkPrimaryButton from "@/Components/LinkPrimaryButton.vue";
const creatingNewSite = ref(null);
const nameInput = ref(null);
const form = useForm({
name: '',
});
const createNewSiteForm = () => {
creatingNewSite.value = true;
}
const createSite = () => {
form.post(route('websites.store'), {
onError: () => nameInput.value.focus(),
});
};
const closeModal = () => {
creatingNewSite.value = false;
form.reset();
};
</script>
<template>
<Head title="Tableau de bord" />
<AuthenticatedLayout>
<template #header>
<PageHeader>Tableau de bord</PageHeader>
<PrimaryButton @click="createNewSiteForm" v-if="$page.props.websites.length != 0">Créer un nouveau site</PrimaryButton>
</template>
<div class="space-y-6">
<article v-for="website in $page.props.websites" class="bg-white flex flex-col lg:flex-row items-center lg:items-start justify-between rounded-lg shadow-md p-4 lg:p-6">
<div class="w-full lg:w-1/2 flex justify-center lg:justify-start">
<div class="relative">
<img src="https://images.unsplash.com/photo-1496128858413-b36217c2ce36?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3603&q=80" alt="" class="aspect-[16/9] w-full rounded-2xl bg-gray-100 object-cover sm:aspect-[2/1] lg:aspect-[3/2]">
<div class="absolute inset-0 rounded-2xl ring-1 ring-inset ring-gray-900/10"></div>
</div>
</div>
<div class="w-full lg:w-1/2 max-w-xl mt-4 lg:mt-0 lg:pl-6 flex flex-col justify-center lg:justify-between">
<div class="flex flex-col justify-center h-full">
<div class="flex items-center gap-x-4 text-xs">
<time datetime="2020-03-16" class="text-gray-500">Dernière modification le 16 mars 2023</time>
</div>
<div class="group relative mt-2">
<h3 class="text-lg font-semibold leading-6 text-gray-900 group-hover:text-gray-600">
<Link :href="route('websites.show', website.slug)">
<span class="absolute inset-0"></span>
{{ website.name }}
</Link>
</h3>
</div>
<div class="mt-4 lg:mt-0 lg:pt-2 flex justify-center lg:justify-start">
<LinkPrimaryButton class="btn-small" :href="route('websites.show', website.slug)">Modifier le site</LinkPrimaryButton>
</div>
</div>
</div>
</article>
<div class="text-center" v-if="$page.props.websites.length == 0">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418" />
</svg>
<h3 class="mt-2 text-sm font-semibold text-gray-900">Aucun site web</h3>
<p class="mt-1 text-sm text-gray-500">Créez un nouveau site web dès maintenant!</p>
<div class="mt-4">
<PrimaryButton class="btn-small" @click="createNewSiteForm">
<svg class="-ml-0.5 mr-1.5 h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z" />
</svg>
Créer un nouveau site web
</PrimaryButton>
</div>
</div>
</div>
<Modal :show="creatingNewSite" @close="closeModal">
<div class="p-6">
<h2 class="text-lg font-medium text-gray-900">
Create site
</h2>
<div class="mt-6">
<InputLabel for="name" value="Site name" />
<TextInput
id="name"
ref="nameInput"
v-model="form.name"
type="name"
class="mt-1 block w-3/4"
@keyup.enter="createSite"
/>
<InputError :message="form.errors.name" class="mt-2" />
</div>
<div class="mt-6 flex justify-end">
<SecondaryButton @click="closeModal"> Cancel </SecondaryButton>
<PrimaryButton
class="ml-3"
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
@click="createSite"
>
Create site
</PrimaryButton>
</div>
</div>
</Modal>
</AuthenticatedLayout>
</template>
public function store(Request $request) {
$data = $request->validate([
'name' => 'required|string|min:1|max:255'
]);
$website = $request->user()->websites()->create($data);
$page = $website->pages()->create([
'name' => 'Accueil'
]);
$website->homepage_id = $page->id;
$website->save();
return to_route('websites.show', $website);
}
<script setup>
import PrimaryButton from "@/Components/PrimaryButton.vue";
import Modal from "@/Components/Modal.vue";
import {useForm, usePage} from "@inertiajs/vue3";
import {ref} from "vue";
import SecondaryButton from "@/Components/SecondaryButton.vue";
import InputError from "@/Components/InputError.vue";
import InputLabel from "@/Components/InputLabel.vue";
import TextInput from "@/Components/TextInput.vue";
import ColorThief from "colorthief";
const {model} = defineProps(['model']);
const creatingEvent = ref(false);
const form = useForm({
name: '',
description: '',
image: '',
place: '',
starts_at: '',
ends_at: '',
inscription_link: ''
})
const fileInput = ref(null);
const currentImage = ref(null);
function onSelectImage($event) {
let file = $event.target.files[0];
form.image = file;
if (!file) {
form.image = '';
return;
}
const reader = new FileReader();
reader.onload = (e) => {
currentImage.value = e.target.result;
};
reader.readAsDataURL(file);
}
function closeModal() {
form.reset();
if(fileInput.value != null) {
fileInput.value.value = null;
}
currentImage.value = null;
creatingEvent.value = false;
}
const store = () => {
form.post(route('events.store', usePage().props.website.slug), {
onError: () => {},
onSuccess: () => {
closeModal();
}
})
}
</script>
<template>
<PrimaryButton @click="creatingEvent = true;">Créer un évènement</PrimaryButton>
<Modal :show="creatingEvent" @close="closeModal">
<div class="p-6">
<h2 class="text-lg font-medium text-gray-900">
Créer un évènement
</h2>
<form @submit.prevent="store">
<img ref="image" :src="currentImage" v-if="currentImage != null" class="max-h-32 mt-6"/>
<div class="mt-6" key="logo">
<InputLabel for="image" value="Image"/>
<input ref="fileInput" type="file" @input="onSelectImage" />
<InputError :message="form.errors.image" class="mt-2" />
</div>
<div class="mt-6">
<InputLabel for="name" value="Nom de l'évènement" />
<TextInput
id="name"
v-model="form.name"
type="text"
class="mt-1 block w-3/4"
placeholder="Nom de l'évènement"
/>
<InputError :message="form.errors.name" class="mt-2" />
</div>
<div class="mt-6">
<InputLabel for="description" value="Description" />
<textarea
id="description"
v-model="form.description"
type="text"
class="textarea mt-1 block w-3/4"
placeholder="Description"
/>
<InputError :message="form.errors.description" class="mt-2" />
</div>
<div class="mt-6">
<InputLabel for="place" value="Lieu" />
<TextInput
id="place"
v-model="form.place"
type="text"
class="textarea mt-1 block w-3/4"
placeholder="Lieu"
/>
<InputError :message="form.errors.place" class="mt-2" />
</div>
<div class="mt-6">
<InputLabel for="starts_at" value="Débute le" />
<input
id="starts_at"
v-model="form.starts_at"
type="datetime-local"
class="input mt-1 block w-3/4"
placeholder="Débute le "
/>
<InputError :message="form.errors.starts_at" class="mt-2" />
</div>
<div class="mt-6">
<InputLabel for="ends_at" value="Termine le" />
<input
id="ends_at"
v-model="form.ends_at"
type="datetime-local"
class="input mt-1 block w-3/4"
placeholder="Termine le "
/>
<InputError :message="form.errors.ends_at" class="mt-2" />
</div>
<div class="mt-6">
<InputLabel for="inscription_link" value="Lien d'inscription" />
<TextInput
id="inscription_link"
v-model="form.inscription_link"
class="input mt-1 block w-3/4"
placeholder="Lien d'inscription"
/>
<InputError :message="form.errors.inscription_link" class="mt-2" />
</div>
<div class="mt-6 flex justify-end">
<SecondaryButton @click="closeModal"> Annuler </SecondaryButton>
<PrimaryButton
class="ml-3"
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
@click="store"
>
Créer un évènement
</PrimaryButton>
</div>
</form>
</div>
</Modal>
</template>
public function store(Website $website, Request $request) {
$data = $request->validate([
'name' => 'required|string|min:1|max:255',
'description' => 'required|string|min:1|max:10000',
'place' => 'required|string|min:1|max:255',
'starts_at' => 'required|date|before:ends_at',
'ends_at' => 'required|date|after:starts_at',
'inscription_link' => 'required|url|max:255',
'image' => 'nullable|image|max:5120'
]);
$event = $website->events()->create(Arr::except($data, 'image'));
$event->uploadImage('image', $request->file('image'), 600);
$event->save();
return to_route('events.index', $website);
}
Please or to participate in this conversation.