diff --git a/packages/astro/components/ViewTransitions.astro b/packages/astro/components/ViewTransitions.astro index 908b35595460..2e8b93c2f100 100644 --- a/packages/astro/components/ViewTransitions.astro +++ b/packages/astro/components/ViewTransitions.astro @@ -89,7 +89,8 @@ const { fallback = 'animate', handleForms } = Astro.props; const form = el as HTMLFormElement; const formData = new FormData(form); - let action = form.action; + // Use the form action, if defined, otherwise fallback to current path. + let action = form.action ?? location.pathname; const options: Options = {}; if (form.method === 'get') { const params = new URLSearchParams(formData as any); diff --git a/packages/astro/e2e/fixtures/view-transitions/src/pages/form-two.astro b/packages/astro/e2e/fixtures/view-transitions/src/pages/form-two.astro new file mode 100644 index 000000000000..01131ee84c4d --- /dev/null +++ b/packages/astro/e2e/fixtures/view-transitions/src/pages/form-two.astro @@ -0,0 +1,17 @@ +--- +import Layout from '../components/Layout.astro'; + +if(Astro.request.method === 'POST') { + const formData = await Astro.request.formData(); + const name = formData.get('name'); + return Astro.redirect(`/form-response?name=${name}`); +} +--- + +

Contact Form

+

This form does not have an `action` defined

+
+ + +
+
diff --git a/packages/astro/e2e/view-transitions.test.js b/packages/astro/e2e/view-transitions.test.js index 41f10a3aa0c5..ac0af3be2332 100644 --- a/packages/astro/e2e/view-transitions.test.js +++ b/packages/astro/e2e/view-transitions.test.js @@ -982,4 +982,26 @@ test.describe('View Transitions', () => { ]); expect(reqUrls).toContainEqual('/one'); }); + + test('form POST with no action handler', async ({ page, astro }) => { + const loads = []; + page.addListener('load', async (p) => { + loads.push(p); + }); + + await page.goto(astro.resolveUrl('/form-two')); + + let locator = page.locator('h2'); + await expect(locator, 'should have content').toHaveText('Contact Form'); + + // Submit the form + await page.click('#submit'); + const span = page.locator('#contact-name'); + await expect(span, 'should have content').toHaveText('Testing'); + + expect( + loads.length, + 'There should be only 1 page load. No additional loads for the form submission' + ).toEqual(1); + }); }); diff --git a/packages/astro/src/transitions/router.ts b/packages/astro/src/transitions/router.ts index b5f198653076..8c008e209176 100644 --- a/packages/astro/src/transitions/router.ts +++ b/packages/astro/src/transitions/router.ts @@ -463,9 +463,10 @@ export function navigate(href: string, options?: Options) { } const toLocation = new URL(href, location.href); // We do not have page transitions on navigations to the same page (intra-page navigation) + // *unless* they are form posts which have side-effects and so need to happen // but we want to handle prevent reload on navigation to the same page // Same page means same origin, path and query params (but maybe different hash) - if (location.origin === toLocation.origin && samePage(toLocation)) { + if (location.origin === toLocation.origin && samePage(toLocation) && !options?.formData) { moveToLocation(toLocation, options?.history === 'replace', true); } else { // different origin will be detected by fetch