How-to: Framer.com pass form submission values to redirect URL
fgbytes
March 5, 2025

Framer is an incredible platform for building websites, but developers often encounter limitations when it comes to form submissions, particularly around handling URL parameters and custom redirects. The standard Framer form submission process can be restrictive, making it difficult to capture and forward important tracking information.
To address these challenges, I've developed a custom React high-order component that provides a flexible solution for form submissions. This override allows developers to seamlessly capture form data, send webhooks, integrate with Google Tag Manager, and redirect users with full parameter preservation.
Full code:
import type { ComponentType } from "react"
import { useEffect } from "react"
// Declare dataLayer for TypeScript
declare global {
interface Window {
dataLayer: any[]
}
}
// Configuration constants
const CONFIG = {
WEBHOOK_URL: 'https://your-webhook-url.com/endpoint', // Replace with your actual webhook URL
REDIRECT_BASE_URL: 'https://your-redirect-url.com/landing', // Replace with your actual redirect URL
ENABLE_WEBHOOK: true, // Toggle webhook functionality
ENABLE_GTM: true, // Toggle Google Tag Manager functionality
GTM_EVENT: {
name: 'begin_checkout', // Event name
type: 'submit_lead', // Event type for logging
},
SOURCE_PREFIX: 'landing_page', // Default source prefix for UTM tracking
}
export function withCustomRedirect(Component: ComponentType): ComponentType {
return (props: any) => {
useEffect(() => {
// Debug log
console.log("Override mounted, props:", props)
// Find and modify the form as soon as it's available
const findAndModifyForm = () => {
const form = document.querySelector("form")
if (form) {
console.log("Found form:", form)
// Remove Framer's form handlers
form.removeAttribute("data-framer-form-state")
form.removeAttribute("data-framer")
// Override form submission
form.onsubmit = async (e) => {
e.preventDefault()
e.stopPropagation()
console.log("Form submission intercepted")
const emailInput = form.querySelector(
'input[type="email"]'
) as HTMLInputElement
if (!emailInput || !emailInput.value) {
console.error("No email input found or empty")
return
}
console.log("Email found:", emailInput.value)
// Get current URL parameters
const currentParams = new URLSearchParams(
window.location.search
)
console.log(
"Current URL params:",
Object.fromEntries(currentParams)
)
try {
const webhookData = {
lead: {
email: emailInput.value,
utm_source: currentParams.get("utm_source")
? `${CONFIG.SOURCE_PREFIX}__${currentParams.get("utm_source")}`
: CONFIG.SOURCE_PREFIX,
utm_campaign:
currentParams.get("utm_campaign") || "",
utm_medium:
currentParams.get("utm_medium") || "",
},
}
console.log("Sending webhook data:", webhookData)
// Push to dataLayer if enabled
if (CONFIG.ENABLE_GTM && window.dataLayer) {
window.dataLayer.push({
event: CONFIG.GTM_EVENT.name,
lead_email: emailInput.value,
lead_source: webhookData.lead.utm_source,
lead_campaign: webhookData.lead.utm_campaign,
lead_medium: webhookData.lead.utm_medium,
})
console.log(`GTM event pushed: ${CONFIG.GTM_EVENT.type}`)
}
// Send webhook if enabled
if (CONFIG.ENABLE_WEBHOOK) {
await fetch(CONFIG.WEBHOOK_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(webhookData),
})
}
// Prepare redirect URL
const queryParams = new URLSearchParams(currentParams)
queryParams.set("email", emailInput.value)
const redirectUrl = `${CONFIG.REDIRECT_BASE_URL}?${queryParams.toString()}`
console.log("Redirecting to:", redirectUrl)
// Force redirect
window.location.href = redirectUrl
} catch (error) {
console.error("Error during submission:", error)
}
}
// Also handle the button click
const button = form.querySelector('button[type="submit"]')
if (button) {
console.log("Found submit button:", button)
button.onclick = (e) => {
e.preventDefault()
e.stopPropagation()
form.onsubmit?.(e as any)
}
}
}
}
// Initial check
findAndModifyForm()
// Set up a mutation observer to watch for form addition
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.addedNodes.length) {
findAndModifyForm()
}
}
})
observer.observe(document.body, {
childList: true,
subtree: true,
})
return () => observer.disconnect()
}, [])
return <Component {...props} />
}
}
How the Override Works: Key Features and Configuration
Configuration Options
WEBHOOK_URL
: The endpoint where form submission data will be sent. Replace with your actual webhook URL.REDIRECT_BASE_URL
: The base URL where users will be redirected after form submission.ENABLE_WEBHOOK
: Toggle to enable or disable webhook functionality (default: true), just leave false if you don't need it.ENABLE_GTM
: Control Google Tag Manager event tracking (default: true).GTM_EVENT
:name
: The event name for GTM tracking (e.g., 'begin_checkout')type
: A secondary event type for logging purposes
SOURCE_PREFIX
: A prefix added to UTM source for additional tracking granularity.
// Configuration constants
const CONFIG = {
WEBHOOK_URL: 'https://your-webhook-url.com/endpoint', // Replace with your actual webhook URL
REDIRECT_BASE_URL: 'https://your-redirect-url.com/landing', // Replace with your actual redirect URL
ENABLE_WEBHOOK: true, // Toggle webhook functionality
ENABLE_GTM: true, // Toggle Google Tag Manager functionality
GTM_EVENT: {
name: 'begin_checkout', // Event name
type: 'submit_lead', // Event type for logging
},
SOURCE_PREFIX: 'landing_page', // Default source prefix for UTM tracking
}
Core Functionality
- Intercepts form submission process
- Removes Framer's default form handling
- Captures email input
- Preserves and forwards URL parameters
- Sends optional webhook with lead information
- Pushes events to Google Tag Manager
- Redirects to a specified landing page with all original parameters
Technical Highlights
- Uses React's
useEffect
for dynamic form modification - Implements a MutationObserver to handle dynamically loaded forms
- Provides TypeScript support
- Configurable and easy to attach to any Framer project
By implementing this override, developers can gain granular control over form submissions, tracking, and user redirects without being constrained by Framer's default behavior.
Still have questions? Just ping me in our Locals.ai community here: https://locals.org/today-i-learned-hxfy