Launcher

Type to filter results. Use arrow keys to navigate, Enter to select.

Preferences

You've reached the end of the results

Contact Form Setup & Resend Integration

Learn how to customize your contact form fields, radio/checkbox options, and set up Resend email service with Vercel environment variables — including API key security best practices.

Pete

Pete

PetePa.com

Introduction

The Contact form is one of the few features in Palo that touches both src/contactForm.ts (field labels, choices) and src/config.yaml (local email delivery settings). This guide walks through every customization point — from renaming labels to wiring up Resend on Vercel or Cloudflare Pages.

Contact 表单是 Palo 中少数同时涉及 src/contactForm.ts(字段标签、选项)和 src/config.yaml(本地邮件投递设置)的功能。本指南涵盖所有自定义点——从修改标签到在 Vercel 或 Cloudflare Pages 上配置 Resend。

What you wantWhere to edit
Change labels, placeholders, error messagessrc/contactForm.ts → fields
Add/remove radio or checkbox optionssrc/contactForm.ts → dynamicSections
Hide a section entirelysrc/contactForm.ts → enable: false
Change recipient emailsrc/config.yaml → contact.receiveEmail
Set up Resend API keyHosting platform → Environment Variables
想做什么在哪修改
修改标签、占位符、错误提示src/contactForm.ts → fields
增删单选/复选项src/contactForm.ts → dynamicSections
完全隐藏某个区域src/contactForm.ts → enable: false
修改收件邮箱src/config.yaml → contact.receiveEmail
配置 Resend API key部署平台 → 环境变量

Architecture Overview

The contact system has three moving parts:

联系系统由三个部分组成:

User fills form  →  Frontend validates  →  POST /api/send-email  →  Resend delivers email
     ↑                    ↑                    ↑                      ↑
contact.astro     contactForm.ts      platform route + shared module     platform env
LayerFileResponsibility
UIsrc/pages/contact.astroRenders form, client-side validation, AJAX submit
Configsrc/contactForm.tsAll labels, options, enable/disable toggles
APIapi/send-email.ts, functions/api/send-email.ts, scripts/vite-plugin-contact-dev-api.tsPlatform request adapters; Vercel’s handler stays self-contained for its function runtime
Deliverysrc/utils/contact-email.ts + platform envShared validation, email building, and Resend delivery for Cloudflare Pages and local development
层级文件职责
UIsrc/pages/contact.astro渲染表单、客户端校验、AJAX 提交
配置src/contactForm.ts所有标签、选项、开关
APIapi/send-email.ts、functions/api/send-email.ts、scripts/vite-plugin-contact-dev-api.ts各部署平台的请求入口;Vercel 为兼容函数运行时保留自包含处理器
投递src/utils/contact-email.ts + 平台环境变量Cloudflare Pages 与本地开发共用的校验、邮件构建和 Resend 投递

Customizing Form Fields

All static text fields live in src/contactForm.ts under fields.

所有静态文本字段都在 src/contactForm.ts 的 fields 下。

fields: {
  name: {
    label: 'YOUR NAME',              // Floating label inside the input
    emptyMsg: 'Please enter your name',  // Error shown after failed submit
  },
  email: {
    label: 'EMAIL ADDRESS',
    emptyMsg: 'Please enter your email address',
    invalidMsg: 'Please enter a valid email address',
  },
  message: {
    label: 'YOUR PROJECT OR IDEA',
    emptyMsg: 'Please tell me about your project or idea',
    placeholder: 'Tell me about your product goals, budget scope, or timeline...',
  },
},
PropertyTypeDescription
labelstringFloating label displayed inside the input
emptyMsgstringError shown when the field is empty
invalidMsgstringError for email format validation (email field only)
placeholderstringPlaceholder text visible when textarea is focused
属性类型说明
labelstring输入框内的浮动标签
emptyMsgstring字段为空时的错误提示
invalidMsgstring邮箱格式错误提示(仅 email 字段)
placeholderstring文本区聚焦时显示的占位文字

Example: Translate to Chinese

fields: {
  name: {
    label: '您的姓名',
    emptyMsg: '请输入您的姓名',
  },
  email: {
    label: '电子邮箱',
    emptyMsg: '请输入您的邮箱',
    invalidMsg: '请输入有效的邮箱地址',
  },
  message: {
    label: '您的项目或想法',
    emptyMsg: '请告诉我您的项目或想法',
    placeholder: '请描述您的产品目标、预算范围或时间线...',
  },
},

Customizing Radio (Identity) and Checkbox (Collaboration)

The two dynamic sections — Identity (radio) and Collaboration (checkbox) — are configured via dynamicSections.

两个动态区域——身份识别(单选)和协作内容(多选)——通过 dynamicSections 配置。

Radio Section: Identity

单选区域:身份识别

identity: {
  enable: true,                    // false = hide the section entirely
  legend: 'WHO ARE YOU?',          // Heading inside the fieldset border
  options: [
    { value: 'creator', label: 'Content Creator' },
    { value: 'designer', label: 'Designer / Artist' },
    { value: 'musician', label: 'Musician / Band Leader' },
    { value: 'dev', label: 'Passionate Dev' },
    { value: 'kindred', label: 'Just a kindred spirit' },
  ],
},

Checkbox Section: Collaboration

多选区域:协作内容

collaboration: {
  enable: true,
  legend: "WHAT'S ON YOUR MIND?",
  options: [
    { value: 'visual', label: 'Visual Identity & Branding' },
    { value: 'web', label: 'Interactive Web Experiences' },
    { value: 'motion', label: 'Motion Typography & Video' },
    { value: 'audiovisual', label: 'Audio-Visual / Music Project' },
    { value: 'chat', label: 'Coffee & Late-night chat' },
  ],
},

Adding a New Option

添加新选项

options: [
  { value: 'creator', label: 'Content Creator' },
  { value: 'designer', label: 'Designer / Artist' },
  // Add your new option here:
  { value: 'photographer', label: 'Photographer' },
  { value: 'musician', label: 'Musician / Band Leader' },
],

Important: The value field is machine-readable and sent to the API. Use short, lowercase, hyphen-free strings. The label is what users see.

注意: value 字段是机器可读的,会发送给 API。使用简短的、小写的、无连字符的字符串。label 是用户看到的内容。

Hiding a Section

隐藏某个区域

Set enable: false to completely hide either section. The frontend will skip rendering, and the API will skip including it in the email.

设置 enable: false 可完全隐藏对应区域。前端会跳过渲染,API 也会跳过在邮件中包含该部分。

identity: {
  enable: false,  // Radio section hidden
  legend: 'WHO ARE YOU?',
  options: [ /* ... */ ],
},

When both sections are hidden, the form becomes a simple 3-field contact form (Name, Email, Message) — perfect for minimal sites.

当两个区域都被隐藏时,表单变为简单的 3 字段联系表单(姓名、邮箱、留言)——非常适合极简风格的网站。

How Options Flow into the Email

选项如何流转到邮件中

The shared delivery module reads labels directly from src/contactForm.ts. Adding, removing, or renaming an option updates the email automatically; a section set to enable: false is also omitted from the email.

共用投递模块会直接读取 src/contactForm.ts 中的标签。新增、删除或重命名选项会自动反映在邮件中;设置为 enable: false 的区域也不会出现在邮件里。


Resend Configuration

Resend is the email delivery service Palo uses. Configuration lives in two places:

Resend 是 Palo 使用的邮件投递服务。配置分布在两个位置:

  1. src/config.yaml — for recipient email and local API key override

  2. Vercel or Cloudflare Pages environment variables — for the production API key (secure)

  3. src/config.yaml — 收件邮箱和本地 API key 覆盖

  4. Vercel 或 Cloudflare Pages 环境变量 — 生产环境 API key(安全存储)

config.yaml Settings

contact:
  receiveEmail: "contact@yourdomain.com"       # Where you receive notifications
  resendDomain: "mail.yourdomain.com"            # Verified Resend domain
  resendFromLocalPart: "hello"                   # Sends as hello@mail.yourdomain.com
  resendApiKey: ""                               # Local dev only — leave empty in production
KeyRequiredDescription
receiveEmailYesThe inbox that receives contact form submissions
resendDomainRecommendedA verified Resend domain, without https:// or an @ prefix.
resendFromLocalPartOptionalThe local part of the sender address; defaults to hello.
resendFromEmailLegacyA full sender address retained for older configurations; resendDomain takes precedence.
resendApiKeyLocal onlyOnly use for local testing. Never commit this to a public repo.
键必填说明
receiveEmail是接收表单提交的邮箱地址
resendDomain推荐Resend 中已验证的域名;不要包含 https:// 或 @。
resendFromLocalPart可选发件邮箱的前缀;默认是 hello。
resendFromEmail兼容旧配置完整发件邮箱;resendDomain 优先。
resendApiKey仅本地仅用于本地测试。 切勿提交到公共仓库。

API Key Resolution Order

API key 解析顺序

The API resolves the key in this order (first match wins):

API 按以下顺序解析 key(第一个匹配生效):

  1. Local development: config.yaml → contact.resendApiKey, then process.env.RESEND_API_KEY

  2. Vercel and Cloudflare Pages: the platform’s RESEND_API_KEY environment variable

  3. 本地开发:config.yaml → contact.resendApiKey,然后是 process.env.RESEND_API_KEY

  4. Vercel 与 Cloudflare Pages:平台中的 RESEND_API_KEY 环境变量


Setting Up Resend on Vercel

This is the recommended production setup.

这是推荐的生产环境配置方式。

Step 1: Create a Resend Account

  1. Go to resend.com and sign up
  2. Verify your email address
  3. On the free tier, you can add 1 domain and send up to 100 emails/day

第一步:创建 Resend 账户

  1. 访问 resend.com 并注册
  2. 验证邮箱地址
  3. 免费版可添加 1 个域名,每天最多发送 100 封邮件

⚠️ Resend Free Tier Limit: One Resend account can only add one domain for free. If you need to send from multiple domains, you’ll need a paid plan ($24/year as of 2026).

⚠️ Resend 免费版限制: 一个 Resend 账户只能免费添加 一个域名。如果需要从多个域名发送邮件,需要付费套餐(2026 年为 $24/年)。

Step 2: Add Your Domain

  1. In Resend Dashboard → Domains → Add Domain
  2. Enter your domain (e.g., yourdomain.com)
  3. Follow the DNS setup instructions (add MX, SPF, DKIM records to your domain registrar)
  4. Wait for domain verification (usually a few minutes)

第二步:添加域名

  1. 在 Resend 控制台 → Domains → Add Domain
  2. 输入你的域名(如 yourdomain.com)
  3. 按照 DNS 设置说明操作(在域名注册商添加 MX、SPF、DKIM 记录)
  4. 等待域名验证通过(通常几分钟)

Step 3: Create an API Key

  1. In Resend Dashboard → Settings → API Keys → Create API Key
  2. Give it a name (e.g., Vercel Production)
  3. Copy the key — it looks like re_xxxxxxxxxxxxxxxxxxxxxxxxx
  4. You won’t see it again after closing the modal!

第三步:创建 API Key

  1. 在 Resend 控制台 → Settings → API Keys → Create API Key
  2. 填写名称(如 Vercel Production)
  3. 复制 key——格式为 re_xxxxxxxxxxxxxxxxxxxxxxxxx
  4. 关闭弹窗后将无法再查看完整 key!

Step 4: Add Environment Variable to Vercel

第四步:在 Vercel 添加环境变量

  1. Go to your Vercel Project → Settings → Environment Variables
  2. Add a new variable:
FieldValue
KeyRESEND_API_KEY
Valuere_xxxxxxxxxxxxxxxxxxxxxxxxx (your Resend API key)
EnvironmentSelect all: Production, Preview, Development
字段值
KeyRESEND_API_KEY
Valuere_xxxxxxxxxxxxxxxxxxxxxxxxx(你的 Resend API key)
Environment全部勾选:Production, Preview, Development
  1. Click Save

  2. Redeploy your site (push any commit, or use Vercel’s Redeploy button)

  3. 点击 Save

  4. 重新部署站点(推送任意 commit,或使用 Vercel 的 Redeploy 按钮)

Step 5: Configure config.yaml

第五步:配置 config.yaml

contact:
  receiveEmail: "hello@yourdomain.com"
  resendDomain: "mail.yourdomain.com"
  resendFromLocalPart: "hello"
  resendApiKey: ""    # Leave empty — Vercel handles it

For local testing only, you can temporarily paste the API key:

仅本地测试时,可临时粘贴 API key:

contact:
  receiveEmail: "hello@yourdomain.com"
  resendDomain: "mail.yourdomain.com"
  resendFromLocalPart: "hello"
  resendApiKey: "re_xxxxxxxxxxxxxxxxxxxxxxxxx"  # TEMPORARY — remove before commit!

API Key Security Warning

Never commit your Resend API key to a public repository.

切勿将 Resend API key 提交到公共仓库。

If your project is open-source and you hardcode the API key in config.yaml, anyone who finds your repo can:

如果你的项目是开源的,且你在 config.yaml 中硬编码了 API key,任何看到仓库的人都可以:

  • Send unlimited emails using your quota

  • Spam others from your domain (ruining your sender reputation)

  • Incur unexpected charges on your Resend account

  • Access the inbox of your receiveEmail (if they craft a malicious form)

  • 使用你的配额无限制发送邮件

  • 从你的域名发送垃圾邮件(损害发件人信誉)

  • 在你的 Resend 账户上产生意外费用

  • 访问你 receiveEmail 的收件箱(如果他们构造了恶意表单)

Safe Practices

安全最佳实践

  1. Production: Always use Vercel Environment Variables (RESEND_API_KEY) — never hardcode

  2. Local testing: Paste the key in config.yaml → contact.resendApiKey, then remove it before committing

  3. Add .env to .gitignore — never commit environment-specific config

  4. Rotate keys immediately if you suspect a leak

  5. Monitor your Resend dashboard for unexpected activity

  6. 生产环境: 始终使用 Vercel 环境变量(RESEND_API_KEY)——绝不硬编码

  7. 本地测试: 在 config.yaml → contact.resendApiKey 中临时粘贴,然后提交前务必清除

  8. 将 .env 加入 .gitignore —— 绝不提交环境相关配置

  9. 立即更换 key 如果你怀疑泄露了

  10. 监控 Resend 控制台的异常活动


Testing the Contact Form

1. Local Development

# In config.yaml, set:
# contact.resendApiKey: "re_xxxxxxxxxxxxxxxxxxxxxxxxx"
npm run dev
# Visit http://localhost:4321/contact
# Fill and submit the form

Check your inbox — the email should arrive within a few seconds.

检查收件箱——邮件应在几秒内到达。

2. Production (Vercel or Cloudflare Pages)

  1. Push your changes to GitHub
  2. Wait for the selected platform to build and deploy
  3. Visit your production URL → Contact page
  4. Submit a test form

Watch the platform logs for the /api/send-email route to confirm delivery.

查看部署平台的 /api/send-email 路由日志以确认投递状态。

3. Troubleshooting

If delivery fails, check the Vercel Function or Cloudflare Pages Function log for the /api/send-email route. Resend’s response is written there, but not exposed to site visitors.

如果投递失败,请查看 Vercel Function 或 Cloudflare Pages Function 中 /api/send-email 路由的日志。Resend 的响应会记录在日志中,但不会暴露给网站访客。


Email Format

The API builds a clean, responsive HTML email:

API 会构建简洁、响应式的 HTML 邮件:

New Contact Inquiry
─────────────────────
Name: Jane Doe
Email: jane@example.com
Identity: Designer / Artist
Topics of Interest:
  • Visual Identity & Branding
  • Interactive Web Experiences

Message:
  I'd love to work with you on a brand redesign...

The email respects your contactForm.ts configuration — disabled sections (identity, collaboration) are automatically excluded from the email body.

邮件遵循 contactForm.ts 的配置——被禁用的区域(identity、collaboration)会自动从邮件正文中排除。


Thank You Page

After successful submission, the user is redirected to /thank-you. You can customize this page at src/pages/thank-you.astro.

提交成功后,用户会被重定向到 /thank-you。可在 src/pages/thank-you.astro 自定义该页面。


Quick Reference

TaskFileKey/Property
Change field labelssrc/contactForm.tsfields.*.label
Change error messagessrc/contactForm.tsfields.*.emptyMsg, fields.email.invalidMsg
Change placeholdersrc/contactForm.tsfields.message.placeholder
Add radio optionsrc/contactForm.tsdynamicSections.identity.options
Add checkbox optionsrc/contactForm.tsdynamicSections.collaboration.options
Hide identity sectionsrc/contactForm.tsdynamicSections.identity.enable: false
Hide collaboration sectionsrc/contactForm.tsdynamicSections.collaboration.enable: false
Change recipientsrc/config.yamlcontact.receiveEmail
Change sender domainsrc/config.yamlcontact.resendDomain + contact.resendFromLocalPart
Set API key (prod)Vercel or Cloudflare PagesRESEND_API_KEY env var
Set API key (local)src/config.yamlcontact.resendApiKey
Change delivery behaviorsrc/utils/contact-email.tsshared delivery module
任务文件键/属性
修改字段标签src/contactForm.tsfields.*.label
修改错误提示src/contactForm.tsfields.*.emptyMsg, fields.email.invalidMsg
修改占位符src/contactForm.tsfields.message.placeholder
添加单选项src/contactForm.tsdynamicSections.identity.options
添加复选项src/contactForm.tsdynamicSections.collaboration.options
隐藏身份区域src/contactForm.tsdynamicSections.identity.enable: false
隐藏协作区域src/contactForm.tsdynamicSections.collaboration.enable: false
修改收件邮箱src/config.yamlcontact.receiveEmail
修改发件域名src/config.yamlcontact.resendDomain + contact.resendFromLocalPart
设置 API key(生产)Vercel 或 Cloudflare PagesRESEND_API_KEY 环境变量
设置 API key(本地)src/config.yamlcontact.resendApiKey
修改投递行为src/utils/contact-email.ts共用投递模块

AI-Assisted Setup

You can describe changes in plain language and let AI edit the files for you:

你可以用自然语言描述变更,让 AI 帮你编辑文件:

“Translate all contact form labels to Chinese”

“Hide the identity radio section and add a ‘Pricing’ checkbox option”

“Change receiveEmail to ‘hello@mydomain.com’ and set resendFromEmail to match”

“Help me set up Resend on Vercel or Cloudflare Pages — walk me through the steps”


Conclusion

The Palo contact form balances simplicity (no code required for basic changes) with flexibility (full control over options and delivery). Remember:

Palo 联系表单在简洁性(基本修改无需代码)和灵活性(完全控制选项和投递)之间取得了平衡。请记住:

  • contactForm.ts controls what users see — labels, options, sections

  • config.yaml controls where emails go — recipient, sender, local API key

  • Vercel or Cloudflare Pages environment variables keep production credentials secure

  • Resend handles delivery with a generous free tier (1 domain, 100 emails/day)

  • contactForm.ts 控制用户看到的内容——标签、选项、区域

  • config.yaml 控制邮件投递目标——收件人、发件人、本地 API key

  • Vercel 或 Cloudflare Pages 环境变量 保护生产环境凭证安全

  • Resend 负责邮件投递,免费版提供 1 个域名、每天 100 封邮件

Happy emailing! 📧

On this page