ฟอร์มที่ใช้งานได้โดยไม่ต้องมีแบ็กเอนด์
เติมแอตทริบิวต์เดียวลงในฟอร์มบนเว็บไซต์ static ของคุณ แล้วทุกการส่งจะเข้ามาที่แดชบอร์ด ไม่ต้องใช้ JavaScript ไม่ต้องฝังวิดเจ็ต ไม่ต้องพึ่งบริการเจ้าอื่น และคนที่กรอกก็ไม่ต้องสมัครอะไรทั้งนั้น
Its markup, styles and deploy script are on GitHub — copy it, change the fields, redeploy.
<form harvis-form="contact"> <input name="email" type="email" required> <textarea name="message"></textarea> <button>Send</button> </form>
สามขั้นตอน และหนึ่งในนั้นคือดีพลอยใหม่
ไม่มีตัวสร้างฟอร์มให้ต้องเรียนรู้ ไม่มี endpoint ให้ต้องคัดลอก แอตทริบิวต์นี้คือการตั้งค่าทั้งหมด
- 01
เติมแอตทริบิวต์
ใส่ harvis-form ลงในฟอร์มไหนก็ได้ใน HTML ของคุณ ตั้งชื่อให้มันถ้าไซต์มีฟอร์มมากกว่าหนึ่ง ส่วน action กับ method ไม่ต้องใส่ — เดี๋ยวจะถูกเติมให้ตอนหน้าเว็บถูกส่งออกไป
- 02
ดีพลอยใหม่
ลากโฟลเดอร์เข้ามาอีกครั้ง หรือรัน npx harvis ไม่มีอะไรต้องตั้งค่า ไม่มีคีย์ให้วาง ไม่มีสวิตช์ในแดชบอร์ดที่ต้องไปเปิดก่อน
- 03
อ่านสิ่งที่เข้ามา
รายการที่ส่งเข้ามาจะอยู่ใต้ไซต์ของคุณในแดชบอร์ด เรียงใหม่สุดขึ้นก่อน กดเปิดแถวเพื่ออ่านทั้งหมด หรือดึงทั้งชุดออกมาเป็น CSV ก็ได้
ทั้งหมดคือสองแอตทริบิวต์
harvis เขียนฟอร์มใหม่ตอนมันออกจากเซิร์ฟเวอร์ หน้าเว็บที่คุณเขียนจึงยังเป็นหน้าเว็บที่คุณเขียนอยู่เหมือนเดิม
<form harvis-form="contact"
data-harvis-redirect="/thanks.html">
<input name="email" type="email" required>
<button>Send</button>
</form><form harvis-form="contact"
data-harvis-redirect="/thanks.html"
action="/__harvis/form/contact" method="post">
<input type="hidden" name="_harvis_redirect" value="/thanks.html">
<input type="text" name="_harvis_hp" tabindex="-1" aria-hidden="true" style="…">
<input name="email" type="email" required>
<button>Send</button>
</form>สองแอตทริบิวต์นั้น
- harvis-form
- ทำเครื่องหมายว่าฟอร์มนี้เป็นฟอร์มที่ต้องเก็บข้อมูล ค่าของมันคือชื่อ ฟอร์มหลายอันบนไซต์เดียวจึงไม่ปนกัน ถ้าไม่ใส่ค่า ข้อมูลจะถูกเก็บไว้ใต้ชื่อ “default”
- data-harvis-redirect
- ไม่บังคับ หน้าบนไซต์ของคุณที่จะพาคนไปหลังจากกดส่ง อะไรก็ตามที่ชี้ออกไปนอกไซต์ของคุณจะถูกละเว้น
ถ้า JavaScript เป็นคนสร้างฟอร์ม ให้เขียนเอนด์พอยต์เอง
harvis เติม action ให้ตอนที่หน้าเว็บออกจากเซิร์ฟเวอร์ ฟอร์มที่เพิ่งมีตัวตนหลังบันเดิลของคุณทำงานจึงยังไม่อยู่ในหน้านั้น ไม่มีอะไรให้เติม — คุณจึงเขียนสิ่งที่การเขียนทับนั้นจะเขียนให้เอง มันยังเป็นการ post แบบ HTML ธรรมดา และทุกอย่างอื่นในหน้านี้ยังใช้ได้เหมือนเดิม
- React
- Vue
- Svelte
- Angular
<form action="/__harvis/form/contact" method="post">
<input name="email" type="email" required />
<textarea name="message" />
{/* optional — where to land after sending */}
<input type="hidden" name="_harvis_redirect" value="/thanks" />
{/* optional — the decoy harvis would have added */}
<input
type="text"
name="_harvis_hp"
tabIndex={-1}
autoComplete="off"
aria-hidden="true"
style={{ position: "absolute", left: "-9999px", opacity: 0 }}
/>
<button>Send</button>
</form>- action + method
- คือเอนด์พอยต์ที่ harvis จะเขียนให้ บวกกับ post ส่วนท้ายสุดคือชื่อของฟอร์ม — ตัวพิมพ์เล็ก ตัวเลข และขีดกลาง อย่างอื่นจะถูกเก็บไว้ใต้ “default”
- _harvis_redirect
- ไม่บังคับ และเป็นรูปแบบที่เขียนเองของ data-harvis-redirect ต้องเป็นพาธในเว็บของคุณเอง อะไรที่ชี้ออกไปข้างนอกจะถูกเมิน
- _harvis_hp
- ไม่บังคับ และเป็นสิ่งเดียวที่คุณเสียไปจากการเขียนฟอร์มเอง นั่นคือเหยื่อล่อที่ดักบอต ให้ดันมันออกนอกจอแทนการใช้ display:none และปล่อยว่างไว้ — อะไรที่กรอกช่องนี้จะถูกทิ้ง
- harvis-form
- ไม่ต้องใส่ แอตทริบิวต์นี้คือการขอให้ harvis เขียน action ให้ ซึ่งคุณเพิ่งเขียนเองไปแล้ว
ปล่อยให้เบราว์เซอร์ส่งเอง
ไม่ต้อง preventDefault ไม่ต้อง fetch คำตอบที่ได้คือการรีไดเรกต์ที่เบราว์เซอร์เดินตามเอง และนี่แหละที่ทำให้ฟอร์มยังทำงานได้ตอนสคริปต์ไม่ทำงาน
พรีเรนเดอร์ก็นับเป็น JavaScript
ถ้าบิลด์ของคุณเขียนฟอร์มลงใน HTML แล้วแอปมารับช่วงต่อในเบราว์เซอร์ ก็ยังต้องเขียนฟิลด์พวกนี้ไว้อยู่ดี — ฟิลด์ที่ harvis ใส่ให้ไม่ได้อยู่ในคอมโพเนนต์ของคุณ ไฮเดรชันจึงอาจทิ้งมันไป
Hand it to the agent that wrote your site
If an AI built the site, it can wire the forms up too. Copy the brief below into whatever has your project open — Claude Code, Cursor, Copilot, the chat you built the page in — and it will find the forms you already have and convert them. It covers both cases on this page, so you don't have to know which one you're in.
Wire the forms in this project up to harvis.dev (https://harvis.dev), which collects form submissions for static sites that have no backend. Read all of this before editing anything.
HOW IT WORKS
harvis serves every page of a site it hosts, so it can take a form post on the way past. Marking a <form> with the harvis-form attribute is the whole integration: as the page is served, harvis rewrites that tag to action="/__harvis/form/<name>" method="post", inserts a hidden honeypot field, and turns data-harvis-redirect into a hidden field. The form then posts natively, same-origin, with no JavaScript, no API key and no third-party service. Submissions appear in the site owner's dashboard (https://harvis.dev/dashboard) and are emailed to them.
STEP 1 — FIND THE FORMS
Look for every <form> in the project: .html files, and templates or components if a framework is in use. Skip search boxes and any form that posts to an API this project owns and expects a reply from. For each remaining form, decide which case it is:
- CASE A — the form is in the HTML that gets deployed: plain HTML, or a generator/framework that renders it at build time. This is the common case.
- CASE B — the form only exists once JavaScript has run: a React/Vue/Svelte/Angular component mounted in the browser, or markup a script writes. harvis rewrites the served HTML, so a form that is not in it yet is never rewritten. A form that is prerendered and then hydrated is CASE B too, because the fields harvis inserts are not in the component tree and hydration can discard them.
STEP 2A — CASE A: ADD THE ATTRIBUTE
- Add harvis-form="<name>" to the opening <form> tag. Name it for what it is — contact, signup, feedback. The name must match ^[a-z0-9][a-z0-9_-]{0,39}$ or it is collected under "default". Two forms on one site should not share a name unless they should share one list of submissions.
- Delete that form's existing action and method attributes. harvis overwrites both, so leaving them there only misleads whoever reads the file next.
- Optional: add data-harvis-redirect="/thanks.html" to choose where the visitor lands after sending. It must be a path on this same site and the file must exist in the deploy; anything pointing off-site is ignored. Without it, visitors get a plain harvis confirmation page.
- Do not add a honeypot, _harvis_hp or _harvis_redirect by hand — harvis inserts them, and a second copy is a bug.
STEP 2B — CASE B: WRITE THE ENDPOINT YOURSELF
- Set action="/__harvis/form/<name>" and method="post" on the form, and do NOT add harvis-form: that attribute is a request to rewrite, and here you have written the rewrite yourself.
- Optional redirect: a hidden input named _harvis_redirect whose value is a path on this site.
- Optional honeypot, since nothing will add one for you: an empty text input named _harvis_hp with tabindex -1, autocomplete off, aria-hidden true, positioned off-screen with position:absolute;left:-9999px rather than display:none.
- Let the browser submit it: no onSubmit handler, no preventDefault, no fetch. The reply is a 303 that the browser follows on its own, and intercepting it is what breaks the form when scripts fail.
STEP 3 — IN BOTH CASES
- Every field to be collected needs a name attribute; an input without one is never submitted. Those names become the column headings the owner reads, so prefer name, email and message over field1.
- Remove what is left of any other form service: a Formspree, Getform, Basin or FormSubmit action URL, Netlify's data-netlify attribute and its hidden form-name input, a Web3Forms access_key input, and any handler that POSTed the form somewhere else.
- Keep the client-side validation as it is. required, type="email", minlength and the rest all still work.
- File inputs are dropped: submissions are stored as text and files are not kept. If a form has one, say so rather than leaving it in silently.
- Add no script, SDK, key or config file. There is nothing to install.
- Limits, worth mentioning if a form is likely to meet one: 30 fields per submission, 5,000 characters per field, 64 KB per submission, 60 submissions per site an hour, 20 per visitor an hour, and the newest 1,000 per site are kept.
STEP 4 — DEPLOY AND REPORT
- The attribute only does anything on a served page, so deploy the site again: run npx harvis from the site folder, or tell the user to drag the folder onto https://harvis.dev/drop.
- Then tell the user which files changed, the name you gave each form, and that submissions arrive at https://harvis.dev/dashboard and by email — noting that a site deployed anonymously has no owner to email until it is claimed with the claim link.
- Suggest they send one test submission through the live site.What it tells the agent to do
- Find every form in the project and leave the search boxes and API calls alone.
- Add harvis-form with a sensible name, and strip the action and method that harvis replaces anyway.
- Write the endpoint by hand instead when the form only exists after JavaScript runs.
- Clear out whatever the last form service left behind, and flag a file upload as something that won't be kept.
- Redeploy, then tell you what changed and where the submissions land.
Or leave it in the repo
The same text works as a file — save it as AGENTS.md, CLAUDE.md or a project rule and an agent reads it on its own, so the next form someone adds is collected without anyone asking.
สิ่งที่คุณจะได้
ทุกอย่างข้างล่างนี้เปิดใช้งานให้อยู่แล้ว และไม่มีหน้าตั้งค่าให้ปรับสักอย่าง
ไม่ต้องใช้ JavaScript
ฟอร์มส่งข้อมูลแบบเดียวกับที่ HTML ส่งกันมาตลอด มันยังทำงานได้แม้สคริปต์ถูกบล็อก บนมือถือเครื่องช้า และในเบราว์เซอร์ที่แสดงได้แค่ข้อความ
กรองสแปมให้
ทุกฟอร์มจะถูกส่งออกไปพร้อมฟิลด์ล่อที่คนมองไม่เห็น อะไรก็ตามที่กรอกลงไปจะถูกทิ้งเงียบๆ ส่วนการจำกัดอัตราจะคุมว่าผู้เข้าชมหนึ่งคน และหนึ่งไซต์ ส่งได้มากแค่ไหนต่อชั่วโมง
มีกี่ฟอร์มก็ได้
ตั้งชื่อให้มันซะ — ฟอร์มติดต่อกับฟอร์มสมัครบนไซต์เดียวกันจะมีรายการของตัวเองแยกกัน และคุณกรองกับส่งออกแยกกันได้
หน้าขอบคุณของคุณเอง
ชี้ฟอร์มไปยังหน้าที่คุณเขียนเอง แล้วคนที่กดส่งจะไปโผล่ที่นั่น ถ้าไม่ตั้ง พวกเขาจะเจอหน้ายืนยันแบบเรียบๆ แทน
ส่งออกเมื่อไหร่ก็ได้
ปุ่มเดียวได้ไฟล์ CSV ที่มีคอลัมน์ครบทุกฟิลด์ที่เคยมีใครส่งเข้ามา ฟอร์มที่เพิ่มคำถามระหว่างทางจึงยังเปิดออกมาเป็นตารางเดียวได้
ไม่มีอะไรตามติดใคร
ไม่มีสคริปต์ติดตาม ไม่มีคุกกี้ ไม่มีเจ้าอื่นอยู่ในหน้าเว็บ ที่อยู่ของผู้เข้าชมจะถูกแฮชก่อนเก็บ และเก็บไว้เพียงเพื่อให้การจำกัดอัตราแยกออกว่าสองการส่งต่างกัน
ข้อจำกัด
เหลือเฟือสำหรับฟอร์มติดต่อ แต่แน่นพอที่สคริปต์ซึ่งเล็งมาที่ไซต์คุณจะถมกล่องข้อความไม่ได้
- ฟิลด์ต่อหนึ่งรายการ
- 30
- ความยาวของหนึ่งฟิลด์
- 5,000 ตัวอักษร
- ขนาดของหนึ่งรายการ
- 64 KB
- จำนวนที่ส่งได้ต่อไซต์
- 60 ต่อชั่วโมง
- จำนวนที่ส่งได้ต่อผู้เข้าชม
- 20 ต่อชั่วโมง
- เก็บไว้ต่อไซต์
- 1,000 รายการ ใหม่สุดขึ้นก่อน
คำถาม
สิ่งที่คนถามกันก่อนจะเอาฟอร์มไปใส่ในเว็บไซต์ static
ฟอร์มแบบ static คืออะไร?
คือฟอร์มบนเว็บไซต์ที่ไม่มีเซิร์ฟเวอร์ของตัวเอง ปกติแล้วนั่นแปลว่าฟอร์มไม่มีที่ให้ส่งข้อมูลไป เว็บไซต์ static เลยมักต้องไปยืมบริการฟอร์มของเจ้าอื่นมาใช้ แต่ harvis เป็นคนส่งทุกหน้าของไซต์คุณอยู่แล้ว จึงรับข้อมูลที่ส่งเข้ามาระหว่างทางได้เลย
ต้องเขียนโค้ดเป็นไหม?
แค่เติมคำเดียวลงใน HTML หนึ่งบรรทัดได้ก็พอ ถ้าเว็บของคุณเขียนโดย AI ก็บอกให้มันเติมแอตทริบิวต์ harvis-form ลงในฟอร์ม — มันรู้วิธี เพราะคำแนะนำที่ harvis เผยแพร่ให้ผู้ช่วย AI บอกไว้แบบนั้น
ปิด JavaScript แล้วยังใช้ได้ไหม?
ได้ นั่นแหละคือเหตุผลที่มันถูกสร้างมาแบบนี้ ฟอร์มทำการ post แบบ HTML ธรรมดา แล้วเบราว์เซอร์ก็ตามการรีไดเรกต์ต่อ เหมือนที่ฟอร์มทำงานกันมาตั้งแต่ก่อนจะมี JavaScript
ยังใช้ endpoint ของตัวเองหรือบริการฟอร์มเจ้าอื่นต่อได้ไหม?
ได้ — แค่อย่าเติมแอตทริบิวต์เข้าไป ฟอร์มที่ไม่มี harvis-form จะถูกส่งออกไปตรงตามที่คุณเขียน รวมถึง action ด้วย ส่วนฟอร์มที่มีแอตทริบิวต์นี้ harvis จะแทนที่ action ให้ เพราะการเติมแอตทริบิวต์คือวิธีบอกว่าข้อมูลควรไปที่ไหน
ใช้กับ React, Vue หรือ Svelte ได้ไหม
ได้ แค่มีขั้นตอนเพิ่มอีกนิด ถ้าฟอร์มอยู่ใน HTML ที่บิลด์ของคุณสร้างออกมา ใส่แอตทริบิวต์อย่างเดียวก็พอ แต่ถ้ามันโผล่มาหลัง JavaScript ทำงานแล้ว ให้เขียน action="/__harvis/form/contact" กับ method="post" ลงในคอมโพเนนต์แทน — เอนด์พอยต์เดิม แดชบอร์ดเดิม และคนส่งก็ยังเป็นเบราว์เซอร์เหมือนเดิม
คนแนบไฟล์ได้ไหม?
ยังไม่ได้ ช่องอัปโหลดไฟล์บนฟอร์มที่เก็บข้อมูลยังใช้งานได้สำหรับผู้เข้าชม แต่ไฟล์จะไม่ถูกเก็บไว้ — เก็บเฉพาะฟิลด์ที่เป็นข้อความ
ถ้าลบไซต์ ข้อมูลที่ส่งเข้ามาจะเป็นยังไง?
หายไปด้วย ข้อมูลที่ส่งเข้ามาผูกกับไซต์ ไม่ได้ผูกกับการดีพลอยครั้งไหน ดีพลอยใหม่จึงยังอยู่ครบ แต่ลบไซต์คือหายถาวร ถ้าอยากเก็บไว้ ส่งออกเป็น CSV ก่อน
เอาไซต์ขึ้นออนไลน์แล้วลองดู
เผยแพร่ได้ฟรีและใช้เวลาราวสองวินาที เติมแอตทริบิวต์ลงในฟอร์ม ดีพลอยใหม่ แล้วส่งข้อความทดสอบหาตัวเอง