Skip to content

Commit 2d09b8f

Browse files
testac974claude
andcommitted
Fix email signup for static GitHub Pages hosting
- Remove API route (requires Node.js runtime, won't work on GitHub Pages) - Use Buttondown's public form endpoint instead (no backend needed) - Update to use NEXT_PUBLIC_BUTTONDOWN_USERNAME env var - Update EMAIL_SETUP.md with static hosting instructions - Form works entirely client-side, perfect for GitHub Pages Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 30362e2 commit 2d09b8f

4 files changed

Lines changed: 88 additions & 146 deletions

File tree

website/.env.example

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Email Service Configuration
2-
# Get your Buttondown API key from: https://buttondown.email/settings
3-
EMAIL_SERVICE=buttondown
4-
EMAIL_SERVICE_API_KEY=your_buttondown_api_key_here
2+
# Your Buttondown username (the part after buttondown.email/ in your newsletter URL)
3+
# For example, if your newsletter is at buttondown.email/yourname, use "yourname"
4+
NEXT_PUBLIC_BUTTONDOWN_USERNAME=your_buttondown_username_here

website/EMAIL_SETUP.md

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,45 @@
11
# Email Signup Setup Guide
22

3-
This website uses Buttondown for email subscriptions.
3+
This website uses Buttondown for email subscriptions with **static GitHub Pages hosting** (no backend required).
44

55
## Setup Instructions
66

7-
### 1. Get Your Buttondown API Key
7+
### 1. Get Your Buttondown Username
88

9-
1. Go to [Buttondown Settings](https://buttondown.email/settings)
10-
2. Navigate to the "API" section
11-
3. Copy your API key
9+
1. Go to [Buttondown](https://buttondown.email)
10+
2. Your newsletter URL is: `https://buttondown.email/YOUR_USERNAME`
11+
3. Copy the username part (e.g., if URL is `buttondown.email/agentic-coding`, username is `agentic-coding`)
1212

13-
### 2. Configure Environment Variables
13+
### 2. Configure Environment Variable
1414

1515
1. Open `/workspace/website/.env.local`
16-
2. Add your Buttondown API key:
16+
2. Add your Buttondown username:
1717

1818
```env
19-
EMAIL_SERVICE=buttondown
20-
EMAIL_SERVICE_API_KEY=your_actual_api_key_here
19+
NEXT_PUBLIC_BUTTONDOWN_USERNAME=your_actual_username_here
2120
```
2221

23-
### 3. Deploy to Production
22+
**Important:** This must be a `NEXT_PUBLIC_` variable so it's available in the browser (since GitHub Pages is static-only).
2423

25-
For Vercel deployment:
24+
### 3. For Production (GitHub Pages)
2625

27-
1. Go to your Vercel project settings
28-
2. Navigate to Environment Variables
29-
3. Add:
30-
- `EMAIL_SERVICE` = `buttondown`
31-
- `EMAIL_SERVICE_API_KEY` = `your_buttondown_api_key`
26+
The environment variable needs to be set at **build time**:
27+
28+
**Option A: GitHub Actions (Recommended)**
29+
1. Go to your repository Settings → Secrets and variables → Actions
30+
2. Add a repository variable (not secret): `NEXT_PUBLIC_BUTTONDOWN_USERNAME`
31+
3. Set the value to your Buttondown username
32+
4. Next deployment will pick it up automatically
33+
34+
**Option B: Hardcode it (Simple but less flexible)**
35+
Edit `website/components/email-signup.tsx` and replace:
36+
```typescript
37+
const username = process.env.NEXT_PUBLIC_BUTTONDOWN_USERNAME || "YOUR_BUTTONDOWN_USERNAME"
38+
```
39+
with:
40+
```typescript
41+
const username = "your-actual-username"
42+
```
3243

3344
### 4. Test the Integration
3445

@@ -38,34 +49,33 @@ For Vercel deployment:
3849
4. Enter a test email and submit
3950
5. Check your Buttondown dashboard to confirm the subscriber was added
4051

41-
## API Endpoint
52+
## How It Works
4253

43-
The email signup uses `/api/subscribe` which:
44-
- Validates email addresses
45-
- Subscribes users to Buttondown
46-
- Adds tags: `agentic-coding-playbook`, `part-2-notification`
47-
- Handles errors gracefully
48-
- Shows success/error messages to users
54+
- **No backend required** - Form submits directly to Buttondown's public API
55+
- **Fully static** - Works on GitHub Pages, no server needed
56+
- **No API keys exposed** - Uses Buttondown's public subscription endpoint
57+
- **Tags applied** - Subscribers get tagged with `agentic-coding-playbook`
4958

5059
## Troubleshooting
5160

52-
**"Email service not configured" error:**
53-
- Make sure `EMAIL_SERVICE_API_KEY` is set in `.env.local` (local) or Vercel environment variables (production)
61+
**Form not working:**
62+
- Check that `NEXT_PUBLIC_BUTTONDOWN_USERNAME` is set correctly
63+
- Verify your Buttondown username by visiting `https://buttondown.email/YOUR_USERNAME`
64+
- Check browser console for errors
5465

55-
**"Failed to subscribe" error:**
56-
- Check that your Buttondown API key is valid
57-
- Verify the API key has the correct permissions
58-
- Check Vercel logs for detailed error messages
66+
**"Something went wrong" error:**
67+
- Verify Buttondown username is correct
68+
- Try subscribing directly at `https://buttondown.email/YOUR_USERNAME` to test
5969

6070
**User already subscribed:**
61-
- The API handles this gracefully and shows "You're already on the list!"
71+
- The form handles this gracefully and shows "You're already on the list!"
6272

6373
## Creating the Welcome Email
6474

6575
In Buttondown:
66-
1. Go to EmailsCreate new email
67-
2. Write your welcome message (suggestion below)
68-
3. Set it as an automated welcome email in Settings
76+
1. Go to SettingsEmails
77+
2. Enable "Send a welcome email to new subscribers"
78+
3. Write your welcome message (suggestion below)
6979

7080
### Suggested Welcome Email
7181

@@ -83,7 +93,7 @@ Thanks for subscribing! You're now on the list for updates about The Agentic Cod
8393

8494
**What's available now:**
8595
Part 1: Foundations is live and ready to read:
86-
[Start Reading](https://testaco.github.io/agentic-coding-book)
96+
https://testaco.github.io/agentic-coding-book
8797

8898
**What's coming:**
8999
- Part 2: The Playbook (6-week journey from idea to production)
@@ -96,10 +106,19 @@ I'll only email when there's something worth your time—no spam, no fluff.
96106
Author, The Agentic Coding Playbook
97107
```
98108

99-
## Switching Email Services (Future)
109+
## Technical Details
110+
111+
**Endpoint:** `https://buttondown.email/api/emails/embed-subscribe/{username}`
112+
113+
**Method:** POST with FormData
100114

101-
To switch to a different email service (ConvertKit, Substack, etc.):
115+
**Parameters:**
116+
- `email`: subscriber email address
117+
- `tag`: automatic tag (set to "agentic-coding-playbook")
102118

103-
1. Update the API route in `/workspace/website/app/api/subscribe/route.ts`
104-
2. Add the new service logic (there's a placeholder for this)
105-
3. Update environment variables accordingly
119+
**Benefits of this approach:**
120+
- ✅ No server required (works on static hosting)
121+
- ✅ No API keys to manage or expose
122+
- ✅ Free tier supports 100 subscribers
123+
- ✅ Clean UX with success/error states
124+
- ✅ Handles duplicates gracefully

website/app/api/subscribe/route.ts

Lines changed: 0 additions & 91 deletions
This file was deleted.

website/components/email-signup.tsx

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,39 @@ export function EmailSignup() {
2020
setMessage("")
2121

2222
try {
23-
const response = await fetch("/api/subscribe", {
23+
// Use Buttondown's public form endpoint (no auth required)
24+
// User needs to set their username in .env.local as NEXT_PUBLIC_BUTTONDOWN_USERNAME
25+
const username = process.env.NEXT_PUBLIC_BUTTONDOWN_USERNAME || "YOUR_BUTTONDOWN_USERNAME"
26+
27+
const formData = new FormData()
28+
formData.append("email", email)
29+
formData.append("tag", "agentic-coding-playbook")
30+
31+
const response = await fetch(`https://buttondown.email/api/emails/embed-subscribe/${username}`, {
2432
method: "POST",
25-
headers: {
26-
"Content-Type": "application/json",
27-
},
28-
body: JSON.stringify({ email }),
33+
body: formData,
2934
})
3035

31-
const data = await response.json()
32-
33-
if (response.ok && data.success) {
36+
if (response.ok) {
3437
setStatus("success")
35-
setMessage(data.message || "You're on the list. We'll be in touch.")
38+
setMessage("You're on the list. We'll be in touch.")
3639
setEmail("")
3740
} else {
38-
setStatus("error")
39-
setMessage(data.error || "Something went wrong. Please try again.")
40-
// Reset to idle after showing error
41-
setTimeout(() => setStatus("idle"), 3000)
41+
const text = await response.text()
42+
// Buttondown returns HTML error pages, check for common messages
43+
if (text.includes("already subscribed") || text.includes("Already subscribed")) {
44+
setStatus("success")
45+
setMessage("You're already on the list!")
46+
setEmail("")
47+
} else if (text.includes("invalid") || text.includes("Invalid")) {
48+
setStatus("error")
49+
setMessage("Invalid email address. Please try again.")
50+
setTimeout(() => setStatus("idle"), 3000)
51+
} else {
52+
setStatus("error")
53+
setMessage("Something went wrong. Please try again.")
54+
setTimeout(() => setStatus("idle"), 3000)
55+
}
4256
}
4357
} catch (error) {
4458
console.error("Subscription error:", error)

0 commit comments

Comments
 (0)