Inertia.js v3.7.0 gives the <Form> component two ways to abort a submission that is still in flight: a cancel method you call yourself, and a cancelOnUnmount attribute that fires when the form leaves the page. The release also adds a reactive polling value to usePoll and stops background async visits from being cancelled when you navigate.
cancelOnUnmounton<Form>aborts a submission when the form unmountscancelis now exposed through the<Form>slot props, ref, and contextusePollreturns apollingvalue alongsidestartandstop- Once props keep their value through an instant visit
- Navigating away no longer cancels async visits aimed at other pages
- Fixes for React
back_forwardrestores,replacePropprop identity, and a<Form>unmount crash
What's New
Cancelling a Submission When the Form Unmounts
An in-flight submission outlives the component that started it. Navigating away happens to cancel it, but only as a side effect of the next sync request interrupting the previous visit. Closing a modal interrupts nothing, so a slow avatar upload the user thought they had abandoned still finishes and still gets processed on the server.
The <Form> component now takes a cancelOnUnmount attribute that cancels the request when the form unmounts:
<Form action="/avatar" method="post" cancel-on-unmount> <input type="file" name="avatar" /> <button type="submit">Upload</button></Form>
The same attribute exists in the React and Svelte adapters as cancelOnUnmount. It defaults to false, so existing forms keep the current behavior of letting the request finish. Both it and the cancel method below are covered in the form cancellation docs.
A submission that already succeeded is left alone. To make that work, the pull request also changed the core request handling so that the function handed to onCancelToken becomes a no-op once the response has arrived. Cancelling at that point could not stop anything anyway, since the page is already being updated, but it did fire an onCancel event after onSuccess. This matters for the common modal pattern where onSuccess closes the dialog: the form unmounts a moment after the response lands, and without the guard the unmount hook would cancel a request that had already completed.
PR: #3225
A cancel Method on the <Form> Component
The <Form> component has always used useForm() internally, but it never surfaced that form's cancel method, so there was no clean way to wire up a cancel button on a large upload. cancel is now available through the slot props, the component ref, and the form context, in all three adapters:
<Form action="/avatar" method="post" v-slot="{ processing, cancel }"> <input type="file" name="avatar" /> <button type="submit" :disabled="processing">Upload</button> <button v-if="processing" type="button" @click="cancel">Cancel</button></Form>
The React adapter passes it through the render prop the same way:
<Form action="/avatar" method="post"> {({ processing, cancel }) => ( <> <input type="file" name="avatar" /> <button type="submit" disabled={processing}>Upload</button> {processing && ( <button type="button" onClick={cancel}>Cancel</button> )} </> )}</Form>
PR: #3224
usePoll Reports Whether It Is Running
Polling with usePoll returned start and stop, and nothing else. Rendering a pause and resume toggle meant keeping your own boolean in sync with those two calls and remembering to seed it from the autoStart option. The hook now returns a reactive polling value that tracks it for you:
import { usePoll } from '@inertiajs/vue3' const { start, stop, polling } = usePoll(2000)
<template> <button v-if="polling" @click="stop">Pause updates</button> <button v-else @click="start">Resume updates</button></template>
It reflects whether the poll is running, not whether a request is currently in flight, so it stays true in the gaps between requests. The polling docs show the pause and resume pattern in all three adapters. The initial value comes from autoStart, which defaults to true. In Vue the value is a Ref<boolean>, in React it is useState, and the Svelte adapter moved usePoll to a .svelte.ts file so it can back the value with $state. That last change means Svelte users destructuring the return value lose reactivity, so keep the object and read poll.polling.
PR: #3220
Once Props Survive Instant Visits
Inertia's once props are resolved a single time and then remembered on the client, so later pages that include the same prop reuse the value instead of asking the server to compute it again. Instant visits are the other side of that coin: giving a <Link> or router.visit() the target component name swaps in that page right away while the real request runs in the background.
Put together, they lost data. The placeholder page was built from the shared props and whatever the visit supplied, which dropped both the once prop's value and its registration. A prefetch that had already claimed the prop then resolved with nothing to restore it from, and the prop came back empty.
The router now copies each remembered once prop and its registry entry onto the placeholder page before the swap, unless the visit provided its own value for that prop, in which case the incoming value wins.
PR: #3221
Async Visits Are No Longer Cancelled by Navigation
Inertia cancels in-flight requests when you navigate to a different page, which is what you want for the deferred props and partial reloads belonging to the page you are leaving. The check was too broad. It also killed explicit async visits pointed at other pages, so clicking an async <Link> twice, or navigating elsewhere while an async request was running, cancelled the earlier one.
Cancellation now matches on the request's origin and path, and only cancels requests aimed at the page being navigated away from. Deferred props, partial reloads, router.reload(), and polls all target the current page and are cancelled as before, while background async visits to other pages are left to finish.
PR: #3198
Fixes
Three fixes round out the release:
- React kept stale props after a tab duplicate. Chrome and Firefox report a duplicated tab as a
back_forwardnavigation, so Inertia restores the page from history state, deferred props included. The React adapter discarded that restored page on load, assuming the first update after mount always repeated the server-rendered page, which leftusePage()stuck on the initial props. The adapter now skips only the initial page's own re-render, and holds an update that arrives before the app has mounted so it can replay it. Vue and Svelte were unaffected (#3196). replaceProp()gave every prop a new identity. Building the new props object with a deep clone meantreplaceProp,appendToProp, andprependToPropre-rendered memoized components consuming unrelated props, and cost a full clone per call. A path-aware immutable set now copies only the containers along the path, matching how partial reloads already treat untouched props. Contributed by Svyatoslav Kryukov (#3194).<Form>could throw on unmount. The React adapter defers its dirty-state check throughstartTransition, which is not cancelled on unmount, so aninputorchangeevent fired just before navigating away could runnew FormData(null)and throw aTypeError.getFormDatanow returns an emptyFormDatawhen the form element is gone. Contributed by Niek Nijland, their first contribution to the project (#3203).
Installation
Upgrade through your package manager, using the adapter your app is on:
npm install @inertiajs/vue3@^3.7.0# or @inertiajs/react, @inertiajs/svelte
References
- Full Changelog
- Inertia.js v3.0.0 Is Here with Optimistic Updates, useHttp, and More
- Canceling form submissions in the Inertia.js documentation