How to Integrate Mailchimp with Astro: Complete Guide
Step-by-step guide to integrating Mailchimp with your Astro website. Setup, configuration, and best practices.
How to Integrate Mailchimp with Astro: Complete Guide
Mailchimp is one of the most popular email marketing platforms, used by millions of businesses for newsletters, marketing campaigns, and audience management. Integrating Mailchimp with your Astro site lets you add newsletter signup forms, manage subscriber lists, and trigger automated email sequences. The most common use case is capturing email addresses from your website visitors and adding them to a Mailchimp audience for ongoing engagement.
This guide covers connecting Mailchimp to an Astro project using both the Marketing API for server-side integration and the embedded form approach for simpler setups.
Prerequisites
Before starting, you will need:
- Node.js 18+ installed
- An existing Astro project (any version for embedded forms, SSR for API integration)
- A Mailchimp account (free plan available at mailchimp.com)
- A Mailchimp API key (Account > Extras > API Keys)
- Your Audience ID (also called List ID, found in Audience > Settings > Audience name and defaults)
- Your server prefix (the
usXpart of your API key, e.g.,us21)
Installation
For the API-based integration, install the Mailchimp Marketing SDK:
npm install @mailchimp/mailchimp_marketing
For the embedded form approach, no packages are needed.
If using the API approach, add an SSR adapter:
npx astro add node
Configuration
Environment Variables
Add your Mailchimp credentials to .env:
MAILCHIMP_API_KEY=your_api_key_here-us21
MAILCHIMP_SERVER_PREFIX=us21
MAILCHIMP_AUDIENCE_ID=your_audience_id_here
Creating the Mailchimp Client
// src/lib/mailchimp.ts
import mailchimp from '@mailchimp/mailchimp_marketing';
mailchimp.setConfig({
apiKey: import.meta.env.MAILCHIMP_API_KEY,
server: import.meta.env.MAILCHIMP_SERVER_PREFIX,
});
export { mailchimp };
export const audienceId = import.meta.env.MAILCHIMP_AUDIENCE_ID;
Astro Config for API Integration
// astro.config.mjs
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';
export default defineConfig({
output: 'hybrid',
adapter: node({ mode: 'standalone' }),
});
Common Patterns
Method 1: Server-Side API Integration (Recommended)
Create an API route that adds subscribers to your Mailchimp audience:
// src/pages/api/subscribe.ts
import type { APIRoute } from 'astro';
import { mailchimp, audienceId } from '../../lib/mailchimp';
export const POST: APIRoute = async ({ request }) => {
const formData = await request.formData();
const email = formData.get('email') as string;
const firstName = formData.get('firstName') as string || '';
const lastName = formData.get('lastName') as string || '';
if (!email) {
return new Response(JSON.stringify({ error: 'Email is required' }), {
status: 400,
});
}
try {
await mailchimp.lists.addListMember(audienceId, {
email_address: email,
status: 'subscribed', // Use 'pending' for double opt-in
merge_fields: {
FNAME: firstName,
LNAME: lastName,
},
});
return new Response(JSON.stringify({ success: true }), { status: 200 });
} catch (error: any) {
const errorBody = error.response?.body;
if (errorBody?.title === 'Member Exists') {
return new Response(
JSON.stringify({ error: 'This email is already subscribed.' }),
{ status: 400 }
);
}
return new Response(
JSON.stringify({ error: 'Subscription failed. Please try again.' }),
{ status: 500 }
);
}
};
Build a newsletter form component:
---
// src/components/NewsletterForm.astro
---
<form id="newsletter-form" method="POST" action="/api/subscribe">
<h3>Subscribe to our newsletter</h3>
<input type="text" name="firstName" placeholder="First name" />
<input type="email" name="email" placeholder="Email address" required />
<button type="submit">Subscribe</button>
<p id="form-message" style="display: none;"></p>
</form>
<script>
const form = document.getElementById('newsletter-form') as HTMLFormElement;
const message = document.getElementById('form-message') as HTMLParagraphElement;
form?.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form);
try {
const response = await fetch('/api/subscribe', {
method: 'POST',
body: formData,
});
const result = await response.json();
message.style.display = 'block';
if (response.ok) {
message.textContent = 'Successfully subscribed!';
message.style.color = 'green';
form.reset();
} else {
message.textContent = result.error;
message.style.color = 'red';
}
} catch {
message.style.display = 'block';
message.textContent = 'Something went wrong. Please try again.';
message.style.color = 'red';
}
});
</script>
Method 2: Embedded Mailchimp Form
For static Astro sites without SSR, use Mailchimp's embedded form. Get the form HTML from Mailchimp: Audience > Signup forms > Embedded forms.
---
// src/components/MailchimpEmbed.astro
---
<div id="mc_embed_shell">
<form
action="https://yourdomain.us21.list-manage.com/subscribe/post?u=YOUR_U_VALUE&id=YOUR_ID_VALUE"
method="post"
target="_self"
>
<label for="mce-EMAIL">Email Address</label>
<input type="email" name="EMAIL" id="mce-EMAIL" required />
<!-- Bot protection -->
<div style="position: absolute; left: -5000px;" aria-hidden="true">
<input type="text" name="b_YOUR_U_VALUE_YOUR_ID_VALUE" tabindex="-1" value="" />
</div>
<button type="submit">Subscribe</button>
</form>
</div>
This approach requires no server-side code and works with fully static Astro sites.
Adding Tags to Subscribers
Organize subscribers with tags when they sign up:
// After adding the member
const subscriberHash = require('crypto')
.createHash('md5')
.update(email.toLowerCase())
.digest('hex');
await mailchimp.lists.updateListMemberTags(audienceId, subscriberHash, {
tags: [
{ name: 'Website Signup', status: 'active' },
{ name: 'Blog Reader', status: 'active' },
],
});
Troubleshooting
"Member Exists" error: The email is already in your audience. The API returns a specific error for this case. Handle it gracefully by showing a friendly message instead of a generic error.
API key not working: Verify your API key includes the server prefix (e.g., abc123def-us21). The us21 part tells the SDK which Mailchimp data center to connect to. If the server prefix is wrong, all requests will fail.
Subscribers not receiving emails: Check your Mailchimp Audience > All contacts. If the status shows "Cleaned" or "Unsubscribed," the contact cannot receive emails. New subscribers with status: 'pending' need to confirm via the double opt-in email first.
CORS errors with embedded form: Mailchimp's embedded forms submit directly to Mailchimp's servers. If you get CORS issues, make sure the form action URL is exactly as provided by Mailchimp, and the form uses a standard POST submission (not fetch/AJAX).
TypeScript errors with the SDK: The @mailchimp/mailchimp_marketing package has limited TypeScript definitions. You may need to add // @ts-ignore for some methods or create a type declaration file. The API method names follow the documentation at mailchimp.com/developer.
Rate limiting: Mailchimp allows 10 simultaneous connections per API key. For high-traffic signup forms, consider batching requests or implementing a queue.
Conclusion
Mailchimp integrates with Astro through either the Marketing API for full control or embedded forms for simplicity. The API approach gives you custom form designs, error handling, and the ability to add tags and merge fields programmatically. Use double opt-in (status: 'pending') for better deliverability and compliance, handle the "Member Exists" case gracefully, and add meaningful tags to segment your audience from the start. For static sites, the embedded form works without any server-side code.
Related Articles
How to Use Algolia with Astro: Complete Guide
Step-by-step guide to integrating Algolia with your Astro website.
How to Integrate Auth0 with Astro: Complete Guide
Step-by-step guide to integrating Auth0 with your Astro website. Setup, configuration, and best practices.
How to Use AWS Amplify with Astro: Complete Guide
Step-by-step guide to integrating AWS Amplify with your Astro website.