Embedding an Impact Stack form on another website

Embedding forms as an iframe on another website is nearly always a bad idea.

In some cases, you might want to have Impact Stack form on your website. We nearly always advise against embedding a full Impact Stack form as an iframe on your website. 

  1. iframes have poor accessibility: iframes often disrupt screen readers and keyboard navigation, making it difficult for users with disabilities to interact with your forms.
  2. iframes reduce conversion rates: Main websites usually have lots of distractions like links and videos. You have just successfully persuaded somebody to visit an action or donation page. This might have cost you money. Make sure you give people a clean experience that focuses their attention on the form.
  3. iframes impair good design: it can be challenging to make an embedded form scale well to all screen widths, particularly on pages with complicated layouts.
  4. iframes make it hard to track conversions: most analytics tools struggle with keeping track of progress through forms in iframes.


Use an Impact Stack page for your form and you'll have the best of both worlds.

You'll have a well-designed page that your supporters will recognise as yours.

And you'll have a page that is free from distractions, making it clear to supporters what the action to do on this page is.


Here's what to do instead of an iframe if you really want to use your main website as a starting point for an Impact Stack action.

Having said all that above, there are reasons that some organisations would want to have a button or a form on their main website that can act as a starting point for a supporter completing one of their Impact Stack forms.

This page gives you some approaches to adding a button or a form on another website that a supporter can interact with and start using an Impact Stack form.

The rest of this page is aimed at helping web developers and others who are comfortable with adding code to websites. The code and ideas here are meant as starting point that you can build from. You almost certainly won't just copy and paste the code blocks here straight on to a page on your website.

A link from your main website to an Impact Stack page

This is overwhelmingly the most common approach. A Donate button in your header or a link from your website's Campaigns page to an Impact Stack form is a quick and easy way to take people from your main website to an action or donation page.


Open an Impact Stack donation form using a link with pre-defined values

The next simplest way to open an Impact Stack form from another website is to have a link that use our pre-population fragments. This enables you to fill in bits of the form based on the link used. For example, add #p:donation_interval=1&p:donation_amount=10 to https://demo2.impact-stack.org/donation-2 to create https://demo2.impact-stack.org/donation-2#p:donation_interval=1&p:donation_amount=10 Link to that URL and people clicking on it will see an Impact Stack form with 'one-off' donation interval and donation amount of '10' prefilled. Donation interval must be '1', 'm', or 'y'. Donation amount must be a positive whole number.

You can find out about this approach to building a pre-population link on our help centre or you can use our toolkit to make this easier.

You don't need to include any extra Javascript for this approach. You can style the link to look like a button if you like.


CleanShot 2024-03-26 at 16.20.54.gif


You could have a series of links on your website that open up an Impact Stack page with different amounts and/or donation intervals and give it a level of interactivity through something like a tabs interface.

CleanShot 2024-03-26 at 17.10.18.gif


Here's the basic code for that.

See the code
<a href="https://demo2.impact-stack.org/donation-2#p:donation_interval=1&p:donation_amount=5">
	Make a one-off €5 donation
<a href="https://demo2.impact-stack.org/donation-2#p:donation_interval=1&p:donation_amount=10"
	Make a one-off €10 donation
<a href="https://demo2.impact-stack.org/donation-2#p:donation_interval=m&p:donation_amount=20"
		background-color: #0070f3;
		color: white;
		border-radius: 5px;
		padding: 10px;
		text-decoration: none;
		width: fit-content;
		user-select: none;
	Make a monthly €20 donation


Using a HTML <form> on a non-Impact Stack website

These next two approaches require an HTML form on a page and some extra Javascript to intercept the submission of that form, get the data from the form inputs, construct a URL that will pre-populate an Impact Stack form, and finally open that URL on a supporter's computer.

Here is an example of some Javascript that can do that. As a reminder, this page is aimed at helping web developers. The code below is meant as starting point that you can adapt and as an example so web developers can understand the approach we're describing. You almost certainly won't just copy and paste the code straight on to a page on your website. Read it and adapt it to your needs. Test anything you do with this code very thoroughly.


See the code
document.addEventListener("DOMContentLoaded", () => {
// Feel free to choose another selector to find the forms on your page const forms = Array.from(document.querySelectorAll("form.impact_stack"));
if (forms.length === 0) { console.error("No forms were found."); return; }
for (const form of forms) {
if (form === null || !(form instanceof HTMLFormElement)) { return; }
form.addEventListener("submit", (event) => { event.preventDefault();
if (event.target === null || !(event.target instanceof HTMLFormElement)) { return; }
try { const formData = new FormData(event.target); const actionURL = new URL(event.target.action);
// This makes a pre-population link for Impact Stack: // https://support.impact-stack.org/hc/en-us/articles/115001868166-How-to-pre-populate-forms // The 'keys' here are from the 'name' attribute on the form inputs.
// They should match the 'form key' on your Impact Stack form
actionURL.hash = Array.from(formData.entries()) .filter(([, value]) => typeof value === "string") .map(([key, value]) => {
// Validation checks. You could adjust these or add any other checks you need here // Donation interval must be 1, m or y
if (key === "donation_interval" &&!["1", "m", "y"].includes(value)) { throw new Error(`Invalid donation_interval value: ${value}`); }
// Donation amount must be an integer greater than 0 if (key === "donation_amount" && (!Number.isInteger(Number(value)) || Number(value) <= 0)) { throw new Error(`Invalid donation_amount value: ${value}`); }
return `p:${encodeURIComponent(key)}=${encodeURIComponent(value)}`; }) .join("&");
window.open(actionURL.toString(), "_self"); // Use _blank to open in a new tab } catch (error) { console.error("Form submission error: ", error.message);
// You might want to show an error message on the page here } }); } }); </script>


Donation Form with editable amount and interval

This could be useful if you want to show the user a donation form on your main website that moves them on to an Impact Stack page. We've demonstrated a very basic donation interval picker and donation amount input. You could make this look and function however you want on your own website. Just make sure that the name attribute of your form inputs matches the relevant form key in Impact Stack. You need to include Javascript in your page that handles the form submission and opens an Impact Stack form such as that shown above.


CleanShot 2024-03-26 at 16.25.51.gif

See the code
// Make sure you have Javascript such as in the demo above
<label for="donation-interval">Donation Interval</label>


<option value="1">One-off</option>
<option value="m">Monthly</option>
<option value="y">Yearly</option>


<label for="donation-amount">Donation Amount</label>
placeholder="Donation amount"

<button type="submit">Donate</button>


Petition or other action form

You can use this approach for text fields too. It's not limited to donation forms. This could be useful if you want people to start a form on your main website then switch them over to an Impact Stack form for submission.

This is a form with three visible inputs that can be set by the user. The name attributes of the input fields should match the form key in Impact Stack. You need to include Javascript in your page that handles the form submission and opens an Impact Stack form as demonstrated above.



CleanShot 2024-03-26 at 16.32.12.gif


See the code
// Make sure you have Javascript such as in the demo above

class="impact_stack" action="https://demo2.impact-stack.org/flexible-form" <label for="first-name">First Name</label> <input id="first-name" type="text" name="first_name" placeholder="First name" required="required"> <label for="last-name">Last Name</label> <input id="last-name" type="text" name="last_name" placeholder="Last name" required="required"> <label for="email">Email</label> <input id="email" type="email" name="email" placeholder="Email" required="required"> <button type="submit">Add your name</button>


Adding a progress bar

You could also show a progress bar with the current number of actions taken on an Impact Stack action or actions on a non-Impact Stack website.

So from this Impact Stack action…


...you could have a progress bar on another website that re-uses the number of signatures from Impact Stack. (Obviously this is a very bare-bones demo that hasn't been styled to look good!)

It can work because every Impact Stack action has a separate, published page with some JSON that shows the current number of signatures. The URL uses the format ${domain}/node/${nodeID}/polling – for example, at https://demo2.impact-stack.org/node/216/polling.

Again, here is some demo code for web developers to understand and re-work for their own needs. It uses clientside Javascript to fetch the current number of signatures from an Impact Stack page. You could rework the same idea for your own system that might be server-rendered. It uses the HTML <progress> element for the progress bar.


See the code
<label for="progressBar">
<span id="signaturesText">0 people have</span> taken the action so far
<progress id="progressBar" max="100"

document.addEventListener("DOMContentLoaded", () => {
// You might want to hide the progress bar and only show it again once the data is fetched
// You also might want to animate the progress bar like we do on Impact Stack

* Update a progress bar based on the number of signatures fetched from Impact Stack
* Changes the text of the progress bar and the value of the progress bar based on the number of signatures * The text of the progress bar is updated to "1 person has" or "N people have" depending on the number of signatures
* The maximum value of the progress bar is updated to the number of signatures divided by 0.9 so the progress bar is always 90% full

async function updateProgressBar() {
const signaturesText = document.getElementById("signaturesText"); // Adjust the selector if needed
const progressBar = document.querySelector("progress#progressBar"); // Adjust the selector if needed
if (signaturesText === null || progressBar === null || progressBar instanceof HTMLProgressElement === false) {
console.error("Signatures text or progress bar not found.");
const signaturesNumber = await fetchSignatureNumberFromImpactStack(
// Adjust the domain as needed

// Adjust the node IDs as needed
// ["216", "224"]
if (signaturesNumber === null) {
console.log("Failed to fetch number");

// Formatting the number of signatures to display
const formattedSignaturesNumber = new Intl.NumberFormat(
// Adjust the locale if needed
// "de-DE"
if (signaturesNumber === 1) {
signaturesText.innerText = `${formattedSignaturesNumber} person has`; // Adjust the text if needed
} else {
signaturesText.innerText = `${formattedSignaturesNumber} people have`; // Adjust the text if needed
progressBar.max = signaturesNumber / 0.9; // This will set the maximum value so that the progress bar is always 90% full
progressBar.value = signaturesNumber;

* Fetches the signature number from Impact Stack from an array of node IDs
* @param {string} domain - The Impact Stack domain
* @param {string[]} nodeIDs - An array of node IDs to fetch the signature number for
* @returns {number|null} The signature number, or null if an error occurred

async function fetchSignatureNumberFromImpactStack(domain, nodeIDs) {
let signaturesNumber = 0;
for (const nodeID of nodeIDs) {
const url = `${domain}/node/${nodeID}/polling`;

try {
const response = await fetch(url);

if (!response.ok) {
console.error(`HTTP error! status: ${response.status}`);
return null;
const data = await response.json(); const number = data.pgbar.pgbar_default[0]; // The route to the data signaturesNumber += number;
} catch (error) {
`There was a problem fetching the data from ${url}: ${error}`

return signaturesNumber;

Have more questions? Submit a request