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) => {
    e.preventDefault()

    try {
      await fetch('/api/send-download-link', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          email: email,
        }),
      })
      setSent(true)
    } 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.
              </span>
            </div>
          ) : (
            <>
              <TextField
                type="email"
                name="email"
                aria-label="Email address"
                placeholder="Email address"
                autoComplete="email"
                required
                autoFocus
                className="min-w-0 shrink w-full"
                value={email}
                onChange={(e) => setEmail(e.target.value)}
              />
              <Button
                type="submit"
                variant="solid"
                color="cyan"
                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>
              </Button>
            </>
          )}
        </div>
      </div>
    </form>
  )
}

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 = 'https://api.resend.com/emails'
  const init = {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${context.env.RESEND_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      from: 'PDF Pals <[email protected]>',
      to: requestBody.email,
      subject: 'Your PDF Pals download link',
      html: `<p>Hello. Your PDF Pals download link is ready:</p>
      <p><a href="https://pdfpals.com/latest">https://pdfpals.com/latest</a>
      <p>If you have troubles downloading the app, feel free to send us an email at [email protected]</p>
      <p>Best,</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.