Add each product with its price, choose which customer details to collect and paste the address of your form handler. You get HTML for an order form with quantity boxes and a running total, ready to drop into your site, plus a printable paper order sheet built from the same list for markets, fairs and phone orders.
| Field in the form | Sent to your handler as | Notes |
|---|---|---|
| Quantity box per product | qty_<product> = number | type=number, min 0, whole numbers |
| Customer name | name | Required |
| type=email, checked by the browser | ||
| Phone (optional) | phone | type=tel |
| Delivery address (optional) | address | Textarea |
| Notes (optional) | notes | Textarea |
| Order total | Not sent | The output element is display only; recalculate on your side |
Updated
Plenty of small sellers do not need a full online store. A bakery taking holiday pre-orders, a farm selling boxes of produce or a club selling team kit just needs a list, a quantity next to each item and the buyer's contact details. This generator writes exactly that as plain HTML, so it works on any site builder that accepts custom code.
Form title: Holiday pre-order
Form handler address (where orders are posted): https://example.com/your-form-handler
Optional customer fields: Phone, Delivery address, Notes
The preview does not send anything. Your form posts to the handler address you entered.
Your HTML
<form class="of" action="https://example.com/your-form-handler" method="post">
<h2>Holiday pre-order</h2>
<fieldset>
<legend>Products</legend>
<label class="of-row">
<span>Sourdough loaf ($8.00)</span>
<input type="number" name="qty_sourdough_loaf" min="0" step="1" value="0" data-price="8.00">
</label>
<label class="of-row">
<span>Cinnamon rolls (6) ($12.00)</span>
<input type="number" name="qty_cinnamon_rolls_6" min="0" step="1" value="0" data-price="12.00">
</label>
<label class="of-row">
<span>Apple pie ($22.50)</span>
<input type="number" name="qty_apple_pie" min="0" step="1" value="0" data-price="22.50">
</label>
</fieldset>
<p>Total: $<output name="total" id="of-total">0.00</output></p>
<label>Name <input name="name" required autocomplete="name"></label>
<label>Email <input type="email" name="email" required autocomplete="email"></label>
<label>Phone <input type="tel" name="phone" autocomplete="tel"></label>
<label>Notes <textarea name="notes" rows="2"></textarea></label>
<button type="submit">Send order</button>
</form>
<style>
.of { display: grid; gap: 10px; max-width: 480px; font: 16px system-ui, sans-serif; }
.of label { display: grid; gap: 4px; }
.of .of-row { grid-template-columns: 1fr 80px; align-items: center; }
.of input, .of textarea { padding: 8px; border: 1px solid #94a3b8; border-radius: 6px; font: inherit; }
.of button { padding: 10px; border: 0; border-radius: 6px; background: #111827; color: #fff; font: inherit; }
</style>
<script>
document.querySelector(".of").addEventListener("input", function (e) {
var sum = 0;
this.querySelectorAll("[data-price]").forEach(function (q) {
sum += (Number(q.value) || 0) * Number(q.dataset.price);
});
document.getElementById("of-total").value = sum.toFixed(2);
});
</script>Name, Phone or email
| Product | Price | Qty | Line total |
|---|---|---|---|
| Sourdough loaf | $8.00 | ||
| Cinnamon rolls (6) | $12.00 | ||
| Apple pie | $22.50 |
Order total
Your handler receives quantities, not a trusted total. This small server-side example in JavaScript prices an order from your own price list and ignores anything unknown.
Prices live on your side, so a visitor who edits the page cannot change what they owe.
const PRICES = { qty_sourdough_loaf: 8, qty_cinnamon_rolls_6: 12, qty_apple_pie: 22.5 };
function priceOrder(form) {
let total = 0;
for (const [field, price] of Object.entries(PRICES)) {
const qty = Math.max(0, Math.floor(Number(form[field]) || 0));
total += qty * price;
}
return Math.round(total * 100) / 100;
}
console.log(priceOrder({ qty_sourdough_loaf: "2", qty_apple_pie: "1" })); // 38.5
A static web page cannot send email by itself. The form needs somewhere to post to: a form backend service that forwards submissions to your inbox or a spreadsheet, the form feature of your site builder, or a small script on your own server.
Whichever you use, copy the exact post address it gives you into the handler field above. If it asks for specific field names, rename the fields in the code to match.
At a stall or a school event, a clipboard with a printed order sheet is faster than asking people to open a website. Printing from the same product list keeps the paper and web prices identical.
Collect the sheets, then type the orders into the web form yourself at the end of the day so everything lands in one place.
To the address in the form's action attribute, sent with method post. That address must belong to something that accepts form posts, such as a form backend service or your own server script. This page does not receive or store orders.
The total sits in an output element, and the contents of an output element are not submitted with the form. That is deliberate here: a visitor could edit a number in their browser, so the seller should always calculate the amount from the quantities.
The form still submits without scripts, but the running total is calculated by a short script included in the code. If scripts are off, the total simply stays at zero.
An order form collects products and quantities. A booking form collects a date, a time and a service. For appointments use our booking form generator instead.
Yes. The code uses a small style block with plain class names that start with of-, so you can change colours, fonts and spacing without affecting the rest of your page.
Related: Booking form generator, How to add a contact form, Invoice generator, Packing slip generator.
Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.