How to create a simple email form with Cloudflare Pages and Resend

PDF Pals is a Mac app and it would be awkward to ask visitors to download on a mobile device.

So I figured it would make more sense to show a "Send download link" form instead of the Download button. Mobile visitors can download later when they get back to their Mac.

It looks something like this:

Email Form on Landing Page

I wanted to ship fast, so I picked a super simple tech stack:

Show me the code

Alright. I hope the code is self-explanatory enough 😅

UI Component

// DownloadLinkForm.jsx
import { useState } from 'react'
import { EnvelopeIcon } from '@heroicons/react/24/outline'
import { Button } from './Button'
import { TextField } from './Fields'

export function DownloadLinkForm() {
  const [email, setEmail] = useState('')
  const [sent, setSent] = useState(false)
  const handleSendDownloadLink = async (e) => {

    try {
      await fetch('/api/send-download-link', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        body: JSON.stringify({
          email: email,
    } catch (error) {
      alert('An error occurred. Please try again.')

  return (
    <form method="post" onSubmit={handleSendDownloadLink}>
      <div className="flex flex-wrap gap-x-4 gap-y-4 justify-center lg:justify-start block lg:hidden">
        <div className="bg-gray-800 p-4 justify-center rounded-lg shadow-lg flex flex-col gap-4 w-80 border border-gray-400/10">
          {sent ? (
            <div className="text-white">
              <p className="font-bold mb-2">✅ Email sent.</p>
              <span className="text-white">
                Check your inbox for the download link.
          ) : (
                aria-label="Email address"
                placeholder="Email address"
                className="min-w-0 shrink w-full"
                onChange={(e) => setEmail(}
                className="group px-12 bg-blue-600 w-full"
                <EnvelopeIcon className="inline h-6 w-6 p-1 -mx-1 group-hover:animate-bounce" />
                <span className="mx-2">Send download link</span>

Pages Function

// /functions/api/send-download-link.ts
interface Env {
  RESEND_API_KEY: string

type AppContext = EventContext<Env, any, any>

function makeResponse(code: number, data: string) {
  return new Response(JSON.stringify({ code, data }), {
    status: code,
    headers: {
      'content-type': 'application/json;charset=UTF-8',

interface RequestBody {
  email: string

export async function onRequestPost(context: AppContext): Promise<Response> {
  const { request } = context

  if (!context.env.RESEND_API_KEY) {
    return makeResponse(500, 'App Not Configured')

  if (request.method !== 'POST') {
    return makeResponse(405, 'Method Not Allowed')

  const requestBody = (await request.json()) as RequestBody

  const url = ''
  const init = {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${context.env.RESEND_API_KEY}`,
      'Content-Type': 'application/json',
    body: JSON.stringify({
      from: 'PDF Pals <[email protected]>',
      subject: 'Your PDF Pals download link',
      html: `<p>Hello. Your PDF Pals download link is ready:</p>
      <p><a href=""></a>
      <p>If you have troubles downloading the app, feel free to send us an email at [email protected]</p>
      <p>Daniel<br/> Founder of PDF Pals</p>

  const response = await fetch(url, init)
  return response
If you are new here, PDF Pals is a native macOS app that allows you to chat with local PDFs instantly. Download now.