---
title: "How to add product analytics to a Webflow site"
description: "Track Webflow CTA clicks and confirmed native form submissions in Amplitude. Add stable attributes and verify conversion counts."
canonical: "https://amplitude.com/en-us/build/webflow-product-analytics"
---

# How to add product analytics to a Webflow site

Track Webflow CTA clicks and confirmed native form submissions in Amplitude. Add stable attributes and verify conversion counts.

Source: https://amplitude.com/en-us/build/webflow-product-analytics

---

# How to add product analytics to a Webflow site

Track a Webflow pricing CTA and confirmed native form submissions in Amplitude. Allow about 20 minutes to add the handlers and verify event counts.

- Webflow
- Custom code
- Browser SDK 2
- CDN
- 20 min

Chris Tam · Last updated 19 October 2026

A Webflow site can have several actions that look like a conversion: a CTA click, a submit attempt, and a form success message. [Product analytics](https://amplitude.com/amplitude-analytics) lets you measure each action with a name that tells you what happened.

In this tutorial, I'll track a pricing CTA and observe a native Webflow form's success state. You'll keep Webflow in charge of submission and send the conversion only when the success message appears.

## What you'll build

A `Pricing CTA Clicked` event and a `Demo Request Confirmed` event, with fixed properties and no form field values in your custom events.

## Prerequisites

- A Webflow site you can publish, with a plan or Workspace that permits custom code. Webflow lists eligible plans in its [custom code instructions](https://help.webflow.com/hc/en-us/articles/33961357265299-Custom-code-in-head-and-body-tags).
- Permission to edit Site settings and publish to a Webflow staging domain. Use a test site before changing production.
- Your Amplitude project API key from Settings > Projects > your project > General > Manage next to API Key. A manager or admin can copy it. The key is public and its scope is limited to the minimum needed to ingest data. Keep secret keys out of Webflow.
- Home and Pricing pages with the shared Amplitude head snippet. The Webflow web analytics tutorial supplies that setup; if starting here, use step 1 below.
- A native Webflow Form Block on Pricing, with its default inline Success and Error states. Leave its redirect unset for this example.

A free account covers the Amplitude setup in this tutorial. [Sign up for free](https://app.amplitude.com/signup) to get your project API key. Webflow custom-code access has its own plan requirements.

## 1. Keep one site-wide SDK installation

Open Site settings > Custom code. Put the SDK in Head code once for the whole site, following the [Amplitude snippet Quickstart](https://amplitude.com/docs/get-started/amplitude-quickstart). Replace `AMPLITUDE_API_KEY` in both places with the same project key. Save changes, then publish to the staging domain.

Keep the external script and its configuration together, in this order. Leave `async` and `defer` off this tag so the next script can use Amplitude's queue. Keep one Amplitude integration: remove an old duplicate only after checking which features it owns, including any GTM installation.

The CDN path includes Web Experiment. The replay plugin below records all test sessions. After QA, change `sampleRate` to your intended production rate; `0` requests no replay capture. Check project-level replay settings too, since remote settings can override SDK configuration. Guides and surveys use a separate script.

```html
<!-- Webflow Site settings > Custom code > Head code -->
<!-- Keep lines other Webflow pages added here: replay privacyConfig (/build/webflow-session-replay), engagement after webflowAmplitudeReady (/build/webflow-guides-surveys), experimentConfig before this loader (/build/webflow-web-experiment). -->
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
<script>
(() => {
  if (!window.amplitude || !window.sessionReplay) {
    console.warn("Amplitude did not load. Check the CDN request and site policy.");
    return;
  }
  window.amplitude.add(window.sessionReplay.plugin({ sampleRate: 1 }));
  window.webflowAmplitudeReady = window.amplitude.init("AMPLITUDE_API_KEY", {
    remoteConfig: { fetchRemoteConfig: true },
    autocapture: {
      attribution: true,
      pageViews: { trackHistoryChanges: "pathOnly" },
      sessions: true,
      formInteractions: true,
      fileDownloads: true,
      elementInteractions: false,
      frustrationInteractions: false,
      networkTracking: false,
      webVitals: false
    }
  }).promise;
})();
</script>
```

If an earlier Webflow tutorial already added this head snippet, keep it and skip this paste. This page changes Footer code only. Carry forward any replay privacy settings or engagement plugin registration. For EU projects, use the EU CDN host and `serverZone: "EU"` in the existing initializer.

If your site waits for consent before collection or storage, configure your consent platform before enabling these scripts. The project-specific loader starts SDKs itself, so gate loading the entire Amplitude snippet on consent. Waiting to call your own `init()` while loading the script earlier does not provide that guarantee. Follow [Browser SDK consent guidance](https://amplitude.com/docs/sdks/analytics/browser/cookies-and-consent-management). Use a synthetic test site for the steps below.

## 2. Give the CTA and Form Block stable attributes

Select your Pricing CTA in the Designer. In Element settings > Custom attributes, add `data-analytics` with value `pricing-cta`. Add `data-analytics-form` with value `demo` to the outer Form Block, which contains both the form and its Success message. Webflow's [custom attributes instructions](https://help.webflow.com/hc/en-us/articles/33961389460115-Custom-attributes) show where to add them.

This is the resulting CTA markup. Keep its existing classes and any attributes used by guides or experiments.

```html
<!-- Pricing CTA: add attributes to your existing Link element -->
<a href="#demo" data-analytics="pricing-cta"><span>Request a demo</span></a>
<!-- Native Form Block: add data-analytics-form="demo" in Element settings. -->
```

Use the native Form Block for submission. Leave Webflow's generated `.w-form-done` success element in place.

## 3. Add the conversion handlers in Footer code

Paste this into Site settings > Custom code > Footer code, after any existing code. It runs after the page markup exists. It listens for clicks on the CTA or its children and watches the native success message without cancelling submission.

```html
<!-- Webflow Site settings > Custom code > Footer code -->
<!-- Keep lines other Webflow pages added here: Pricing CTA Clicked and Demo Request Confirmed handlers (webflow-product-analytics); reuse them as experiment metrics and guide triggers. -->
<script>
(() => {
  if (window.__webflowAmplitudeEvents) return;
  window.__webflowAmplitudeEvents = true;

  document.addEventListener("click", (event) => {
    if (!(event.target instanceof Element)) return;
    const cta = event.target.closest('[data-analytics="pricing-cta"]');
    if (!cta || !window.amplitude) return;
    window.amplitude.track("Pricing CTA Clicked", {
      placement: "pricing",
      page_path: window.location.pathname
    });

  document.querySelectorAll('[data-analytics-form="demo"]').forEach((block) => {
    const success = block.querySelector(".w-form-done");
    if (!success) return;
    let wasVisible = getComputedStyle(success).display !== "none";
    const checkSuccess = () => {
      const visible = getComputedStyle(success).display !== "none";
      if (visible && !wasVisible && window.amplitude) {
        window.amplitude.track("Demo Request Confirmed", {
          form_name: "demo",
          page_path: window.location.pathname
        });
      }
      wasVisible = visible;
    };
    new MutationObserver(checkSuccess).observe(success, {
      attributes: true,
      attributeFilter: ["style", "class"]
    });
})();
</script>
```

The guard prevents this footer from registering listeners twice on the same document. On a new page load it registers again, as intended. The observer records the initial display state, then counts each later hidden-to-visible success transition once, even if Webflow also updates another attribute. This is a browser success-state signal, not an independent check that a CRM or other downstream system received the request. It watches forms present when the footer runs; forms inserted later need their own observer setup.

**Why.** A `submit` event means an attempt. A server error can follow it. The success element is the confirmation for this inline native-form flow. If your form redirects or uses a third-party provider, use that provider's documented success callback or backend acknowledgement instead of this observer.

## 4. Test a failure and a success

Publish, then click the CTA once, including its nested text. Try an invalid form first; it should produce no `Demo Request Confirmed`. Submit valid synthetic data and wait for Webflow's Success state. Then run:

Browser Console after the published form shows Success

```javascript
// Browser Console after the published form shows Success
window.amplitude.flush();
window.amplitude.getDeviceId();
```

Check one `Pricing CTA Clicked` with `placement=pricing` and one `Demo Request Confirmed` with `form_name=demo`. `page_path` identifies the page. Autocapture's form event remains a separate attempt signal. In a funnel, use the custom confirmed event as the conversion.

**MCP.** Ask your coding agent to inspect existing event names with the [Amplitude MCP server](https://amplitude.com/docs/amplitude-ai/amplitude-mcp) before adding tracking. Reuse the project's taxonomy when this tutorial's example names already have equivalents.

## Verify it worked

1. **Published site:** Open the staging URL directly. Check that the SDK request succeeds and the Console has no integration errors. Saved custom code reaches the live site only after publishing.
2. **Network:** Confirm an event POST to `api2.amplitude.com` returns `200` for a US project. EU ingestion uses `api.eu.amplitude.com`. Use Network for delivery status; large event batches can be compressed.
3. **Instrumentation Explorer:** Read the event names and properties in the Chrome extension. Note `device_id` and run the action once to check the count.
4. **User profile:** In Amplitude, open Users > User Profiles, search the device ID, and check the event stream. Allow ten seconds to one minute. Data > Sources > Ingestion Debugger shows request and event counts, rather than individual events.

## Troubleshooting

### The form succeeds but the conversion is missing

Put `data-analytics-form="demo"` on the outer Form Block, rather than its inner form. Confirm that `.w-form-done` changes from hidden to visible. Redirecting forms and embedded forms use a different success signal.

### A failed attempt appears as a conversion

Check the event name. The custom conversion is `Demo Request Confirmed`; autocaptured form submissions can still appear for attempts.

### Nothing happens after saving the code

Publish the site and open its staging URL directly. Check Site settings and Page settings for duplicate scripts. A change in the settings editor is not a deployment.

### The SDK request fails or events never upload

Check the key, region, browser extensions, and Console. Follow the relevant SDK's endpoint and CSP guidance for a blocked request; keep existing security policies in place.

### You see two page views for one navigation

Check for a second installation in page code, embeds, and GTM. Keep the site-wide initializer and let autocapture send page views; remove your manual page-view call.

### The observed tracking differs from the snippet

Review Data > Settings > Autocapture. Remote settings take precedence over the local autocapture options when remote configuration is enabled.

## FAQ

### Does autocapture's form submit event prove the request succeeded?

Use the custom success event in this tutorial for that outcome. The footer observes Webflow's inline Success state after submission.

### Should I identify a visitor with their form email?

This example leaves visitors anonymous and sends no form values in its custom events. Add identity through your authenticated user flow when you have a stable account ID.

## Full code reference

This is the full Footer code added by this page. Unchanged from earlier pages; keep your versions: Head code, page navigation, replay privacy settings, engagement registration, and experiment consent configuration.

Full code reference

```html
<!-- Webflow Site settings > Custom code > Footer code -->
<!-- Keep lines other Webflow pages added here: Pricing CTA Clicked and Demo Request Confirmed handlers (webflow-product-analytics); reuse them as experiment metrics and guide triggers. -->
<script>
(() => {
  if (window.__webflowAmplitudeEvents) return;
  window.__webflowAmplitudeEvents = true;

  document.addEventListener("click", (event) => {
    if (!(event.target instanceof Element)) return;
    const cta = event.target.closest('[data-analytics="pricing-cta"]');
    if (!cta || !window.amplitude) return;
    window.amplitude.track("Pricing CTA Clicked", {
      placement: "pricing",
      page_path: window.location.pathname
    });

  document.querySelectorAll('[data-analytics-form="demo"]').forEach((block) => {
    const success = block.querySelector(".w-form-done");
    if (!success) return;
    let wasVisible = getComputedStyle(success).display !== "none";
    const checkSuccess = () => {
      const visible = getComputedStyle(success).display !== "none";
      if (visible && !wasVisible && window.amplitude) {
        window.amplitude.track("Demo Request Confirmed", {
          form_name: "demo",
          page_path: window.location.pathname
        });
      }
      wasVisible = visible;
    };
    new MutationObserver(checkSuccess).observe(success, {
      attributes: true,
      attributeFilter: ["style", "class"]
    });
})();
</script>
```

## Hand it to your agent

Use this prompt with the tutorial content attached.

Using Cursor or Claude Code? Paste this prompt

```text
Agent task: Webflow product analytics.
Preserve the existing Head code, including replay, guides, and experiment additions. Add the product analytics footer once. Put data-analytics=pricing-cta on the CTA and data-analytics-form=demo on the native Form Block. Keep its inline Success state and leave redirect unset. Test a nested CTA click, failed submission, successful submission, repeated success mutations, and a second footer evaluation. Track no field values. Report counts and unresolved hosted tests.
```

## What this unlocks

[Web analytics](https://amplitude.com/web-analytics) connects the page visit to `Demo Request Confirmed`.

[Session replay](https://amplitude.com/session-replay) lets you inspect the form interaction behind the conversion.

[Web Experiment](https://amplitude.com/web-experimentation) uses `Demo Request Confirmed` as the outcome for a CTA change.

[Guides and surveys](https://amplitude.com/guides-and-surveys) can react to `Pricing CTA Clicked` when you configure an event trigger.

## Next steps

[WebflowHow to add web analytics to a Webflow siteA published Webflow site with automatic page views and sessions, plus a test visit attributed to \`utm\_campaign=webflow-build-qa\`.](https://amplitude.com/build/webflow-web-analytics)[WebflowHow to add session replay to a Webflow siteA replay of a Home-to-Pricing visit with synthetic form input masked, a private text region masked, and an excluded region blocked.](https://amplitude.com/build/webflow-session-replay)[WebflowHow to add heatmaps to a Webflow siteHeatmap capture enabled for the Webflow project and a pricing-page map configured for the published URL, with the correct viewport bucket.](https://amplitude.com/build/webflow-heatmaps)

Reference docs

[Amplitude quickstart →](https://amplitude.com/docs/get-started/amplitude-quickstart)[Browser sdk 2 →](https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2)[Debug analytics →](https://amplitude.com/docs/analytics/debug-analytics)[Amplitude mcp →](https://amplitude.com/docs/amplitude-ai/amplitude-mcp)[Amplitude MCP server →](https://amplitude.com/docs/amplitude-ai/amplitude-mcp)

Amplitude is the leading [all-in-one analytics platform](https://amplitude.com/) for building better products. Product and engineering teams use Amplitude to connect [product analytics](https://amplitude.com/amplitude-analytics) and [web analytics](https://amplitude.com/web-analytics) with [session replay](https://amplitude.com/session-replay), [heatmaps](https://amplitude.com/heatmaps), [experimentation](https://amplitude.com/amplitude-experiment), [feature flags](https://amplitude.com/feature-management), and [guides and surveys](https://amplitude.com/guides-and-surveys) in one unified workflow, with [AI agents](https://amplitude.com/ai-agents) working across all of it. Learn how Amplitude is building [self-improving products](https://amplitude.com/self-improving-products) with [Wave](https://amplitude.com/wave), helping teams turn product data into PRs.

For agents

[View raw .md](/build/webflow-product-analytics.md)[llms.txt](/llms.txt)

## Ship the instrumentation, then read the data

Use the verification steps above to check the events and product behavior you enabled.

[Create a free account](https://app.amplitude.com/signup)[Browse all Build tutorials](https://amplitude.com/build)
