Create a HTML form·续

<button type="submit">Submit</button> </form> </body> </html>


This HTML document will contain a form with a few fields for the user to fill out. Because there is no validation rules within the form, all fields are optional and the user is able to submit an empty form. For this example, this is intended behavior.

Optional content

Technically, only the `<form>` and its child elements are necessary. The `<head>` and the enclosing `<html>` and `<body>` tags are optional and not strictly necessary for a valid HTML document.

The HTML page is also completely unstyled at this point, relying on the browsers' default UI and color palettes. Styling the page is entirely optional and not necessary for the form to function. If you would like to attach a CSS stylesheet, you may [add a `<link>` element ↗︎](https://developer.mozilla.org/en-US/docs/Learn/CSS/First_steps/Getting_started#adding_css_to_our_document). Refer to the finished tutorial's [source code ↗︎](https://github.com/cloudflare/submit.pages.dev/blob/8c0594f48681935c268987f2f08bcf3726a74c57/public/index.html#L11) for an example or any inspiration – the only requirement is that your CSS stylesheet also resides within the `public` directory.

### Worker

The HTML form is complete and ready for deployment. When the user submits this form, all data will be sent in a `POST` request to the `/api/submit` URL. This is due to the form's `method` and `action` attributes. However, there is currently no request handler at the `/api/submit` address. You will now create it.

Cloudflare Pages offers a [Functions](https://developers.cloudflare.com/pages/functions/) feature, which allows you to define and deploy Workers for dynamic behaviors.

Functions are linked to the `functions` directory and conveniently construct URL request handlers in relation to the `functions` file structure. For example, the `functions/about.js` file will map to the `/about` URL and `functions/hello/[name].js` will handle the `/hello/:name` URL pattern, where `:name` is any matching URL segment. Refer to the [Functions routing](https://developers.cloudflare.com/pages/functions/routing/) documentation for more information.

To define a handler for `/api/submit`, you must create a `functions/api/submit.js` file. This means that your `functions` and `public` directories should be siblings, with a total project structure similar to the following:

```txt
├── functions
│   └── api
│       └── submit.js
└── public
    └── index.html

The <form> will send POST requests, which means that the functions/api/submit.js file needs to export an onRequestPost handler:

/**
 * POST /api/submit
 */
export async function onRequestPost(context) {
    // TODO: Handle the form submission
}

The context parameter is an object filled with several values of potential interest. For this example, you only need the Request object, which can be accessed through the context.request key.

As mentioned, a <form> defaults to the application/x-www-form-urlencoded MIME type when submitting. And, for more advanced scenarios, the enctype="multipart/form-data" attribute is needed. Luckily, both MIME types can be parsed and treated as FormData ↗︎. This means that with Workers – which includes Pages Functions – you are able to use the native Request.formData ↗︎ parser.

For illustrative purposes, the example application's form handler will reply with all values it received. A Response must always be returned by the handler, too:

/**
 * POST /api/submit
 */
export async function onRequestPost(context) {
    try {
        let input = await context.request.formData();
        let pretty = JSON.stringify([...input], null, 2);
        return new Response(pretty, {
            headers: {
                "Content-Type": "application/json;charset=utf-8",
            },
        });
    } catch (err) {
        return new Response("Error parsing JSON content", { status: 400 });
    }
}

With this handler in place, the example is now fully functional. When a submission is received, the Worker will reply with a JSON list of the FormData key-value pairs.

However, if you want to reply with a JSON object instead of the key-value pairs (an Array of Arrays), then you must do so manually. Recently, JavaScript added the Object.fromEntries ↗︎ utility. This works well in some cases; however, the example <form> includes a movies checklist that allows for multiple values. If using Object.fromEntries, the generated object would only keep one of the movies values, discarding the rest. To avoid this, you must write your own FormData to Object utility instead:

/**
 * POST /api/submit
 */
export async function onRequestPost(context) {
    try {
        let input = await context.request.formData();

// Convert FormData to JSON
        // NOTE: Allows multiple values per key
        let output = {};
        for (let [key, value] of input) {
            let tmp = output[key];
            if (tmp === undefined) {
                output[key] = value;
            } else {
                output[key] = [].concat(tmp, value);
            }
        }

let pretty = JSON.stringify(output, null, 2);
        return new Response(pretty, {
            headers: {
                "Content-Type": "application/json;charset=utf-8",
            },
        });
    } catch (err) {
        return new Response("Error parsing JSON content", { status: 400 });
    }
}

The final snippet (above) allows the Worker to retain all values, returning a JSON response with an accurate representation of the <form> submission.

Deployment

You are now ready to deploy your project.

If you have not already done so, save your progress within git and then push the commit(s) to the GitHub repository:

# Add all files
git add -A
# Commit w/ message
git commit -m "working example"
# Push commit(s) to remote
git push -u origin main

Your work now resides within the GitHub repository, which means that Pages is able to access it too.

If this is your first Cloudflare Pages project, refer to the Get started guide for a complete walkthrough. After selecting the appropriate GitHub repository, you must configure your project with the following build settings:

  • Project name – Your choice
  • Production branch –  main
  • Framework preset – None
  • Build command – None / Empty
  • Build output directory – public

After clicking the Save and Deploy button, your Pages project will begin its first deployment. When successful, you will be presented with a unique *.pages.dev subdomain and a link to your live demo.

In this tutorial, you built and deployed a website and its back-end logic using Cloudflare Pages with its Workers integration. You created a static HTML document with a form that communicates with a Worker handler to parse the submission request(s).

If you would like to review the full source code for this application, you can find it on GitHub ↗︎.

Was this helpful?

YesNo

On this page

Create a HTML form·续 - 图1Docs

{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/pages/tutorials/forms/#page","headline":"Create a HTML form","description":"This tutorial will briefly touch upon the basics of HTML forms. This tutorial will make heavy use of Cloudflare Pages and its Workers integration.","url":"https://developers.cloudflare.com/pages/tutorials/forms/","inLanguage":"en","image":"https://developers.cloudflare.com/pages/tutorials/forms/og.png?v=6cb64de35a9e6885","dateModified":"2026-04-21","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"},"keywords":["Forms","JavaScript"]}


description: This tutorial uses the HTMLRewriter functionality in the Cloudflare Workers platform to overlay an i18n layer, automatically translating the site based on the user's language. title: Localize a website with HTMLRewriter image: https://developers.cloudflare.com/pages/tutorials/localize-a-website/og.png?v=1314db6121d96aa3


Skip to content

Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/pages/llms.txt
Use this file to discover all available pages before exploring further.