Mga form na gumagana kahit walang backend
Magdagdag ng isang attribute sa isang form sa static site mo at mapupunta sa dashboard mo ang bawat submission. Walang JavaScript, walang naka-embed na widget, walang third-party service, at walang kailangang sign-up-an ang taong sumasagot.
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>
Tatlong hakbang, at ang isa doon ay pag-deploy uli
Walang form builder na kailangang matutunan at walang endpoint na kailangang kopyahin. Ang attribute mismo ang buong setup.
- 01
Idagdag ang attribute
Ilagay ang harvis-form sa kahit anong form sa HTML mo. Pangalanan ito kung mahigit isa ang form ng site. Huwag nang ilagay ang action at method — pupunan ang mga iyon habang inihahain ang page.
- 02
I-deploy uli
I-drag uli ang folder, o patakbuhin ang npx harvis. Walang ico-configure, walang key na ipe-paste, walang switch na kailangang buksan muna sa dashboard.
- 03
Basahin ang dumarating
Lalabas ang mga submission sa ilalim ng site mo sa dashboard, pinakabago ang una. Buksan ang isang row para basahin ang buo, o kunin ang lahat bilang CSV.
Dalawang attribute lang ang lahat ng ito
Sinusulat uli ng harvis ang form habang palabas ito ng server, kaya ang page na sinulat mo ay nananatiling ang page na sinulat mo.
<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>Ang dalawang attribute
- harvis-form
- Minamarkahan nito ang form bilang kokolektahin. Ang value ang pangalan nito, kaya nananatiling magkahiwalay ang ilang form sa iisang site; kapag walang value, sa ilalim ng “default” ito kinokolekta.
- data-harvis-redirect
- Opsyonal. Ang page sa site mo kung saan dadalhin ang mga tao pagkatapos nilang mag-submit. Binabalewala ang anumang nakaturo sa labas ng site mo.
Kapag JavaScript ang gumagawa ng form, ikaw na ang sumulat ng endpoint
Pinupunan ng harvis ang action habang paalis ang page sa server. Ang form na lumilitaw lang matapos tumakbo ang bundle mo ay wala pa sa page, kaya walang mapupunan — ikaw na ang sumulat ng dapat sanang isinulat ng rewrite. Ordinaryong HTML post pa rin ito, at tumatagal pa rin ang lahat ng iba pa sa page na ito.
- 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
- Ang endpoint na isusulat sana ng harvis, kasama ang post. Ang huling bahagi ay ang pangalan ng form — maliliit na titik, numero at gitling; anumang iba pa ay naiipon sa ilalim ng “default”.
- _harvis_redirect
- Opsyonal, at ito ang sulat-kamay na anyo ng data-harvis-redirect. Dapat path sa sarili mong site; binabalewala ang anumang nakaturo sa labas.
- _harvis_hp
- Opsyonal, at ito ang tanging nawawala kapag ikaw ang sumulat ng form: ang pain na humuhuli ng bot. Itulak mo ito sa labas ng screen sa halip na display:none, at iwang blangko — itinatapon ang anumang pumupuno rito.
- harvis-form
- Huwag mo nang ilagay. Ang attribute na iyon ay hiling na ang harvis ang sumulat ng action, at ikaw mismo ang kasusulat pa lang nito.
Hayaan ang browser ang magpadala
Walang preventDefault, walang fetch. Redirect ang sagot na susundan ng browser nang kusa, at iyon ang nagpapanatiling gumagana ang form kapag hindi tumatakbo ang mga script.
Bilang JavaScript din ang prerendered
Kung isinusulat ng build mo ang form sa HTML at saka ito kinukuha ng app mo sa browser, isulat mo pa rin ang mga field — wala sa component mo ang mga isiningit ng harvis, kaya pwedeng itapon iyon ng hydration.
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.
Ang makukuha mo
Naka-on na ang lahat ng nasa ibaba. Wala ni isang settings page para sa kahit alin sa mga ito.
Walang JavaScript
Nagsu-submit ang form sa paraang matagal nang ginagawa ng HTML. Gumagana pa rin ito kahit naka-block ang mga script, sa mabagal na telepono, at sa browser na teksto lang ang naipapakita.
Nasasala ang spam
Bawat form ay may kasamang pain na field na hindi nakikita ng tao, at tahimik na tinatapon ang anumang pumupuno rito. May rate limit din na naglilimita kung gaano karami ang kayang ipadala ng isang bisita, at ng isang site, sa loob ng isang oras.
Kasindami ng gusto mong form
Pangalanan mo sila — hiwalay ang listahan ng contact form at ng signup form sa iisang site, at puwede mo silang i-filter at i-export nang magkahiwalay.
Sarili mong thank-you page
Ituro ang form sa isang page na ikaw ang gumawa at doon mapupunta ang mga tao pagkatapos mag-submit. Kung wala nito, simpleng confirmation page ang makikita nila.
Mag-export kahit kailan
Isang button lang at makakakuha ka ng CSV na may column para sa bawat field na naipasa na ninuman, kaya kahit dumagdag ng tanong ang form sa gitna ng buhay nito, iisang table pa rin ang bubukas.
Walang sumusunod kahit kanino
Walang tracking script, walang cookie, walang third party sa loob ng page. Hino-hash ang address ng bisita bago itago, at iyon lang ay para lang makilala ng rate limit ang pagkakaiba ng dalawang submission.
Ang mga limitasyon
Sagana para sa isang contact form, at sapat na sikip para hindi mapuno ng script na nakaturo sa site mo ang inbox mo.
- Field kada submission
- 30
- Haba ng isang field
- 5,000 karakter
- Laki ng isang submission
- 64 KB
- Submission kada site
- 60 kada oras
- Submission kada bisita
- 20 kada oras
- Iniingatan kada site
- 1,000, pinakabago ang una
Mga tanong
Ang itinatanong ng mga tao bago maglagay ng form sa static site.
Ano ang static form?
Isang form sa site na walang sariling server. Karaniwan, ibig sabihin nito ay walang mapagpapadalhan ang form, kaya karamihan sa mga static site ay humihiram ng third-party na form service. Inihahain na ng harvis ang bawat page ng site mo, kaya kaya nitong salubungin ang submission habang dumadaan.
Kailangan ko bang marunong mag-code?
Kailangan mo lang makapagdagdag ng isang salita sa isang linya ng HTML. Kung AI ang gumawa ng site mo, pakiusapan mo itong idagdag ang harvis-form attribute sa form — alam nito kung paano, dahil nakasaad iyon sa mga instruction na inilalathala ng harvis para sa mga AI assistant.
Gumagana ba ito kapag naka-off ang JavaScript?
Oo. Iyon nga ang dahilan kaya ganito ito ginawa. Ordinaryong HTML post ang ginagawa ng form at susundan ng browser ang isang redirect pagkatapos, gaya mismo ng paggana ng mga form bago pa umiral ang JavaScript.
Puwede ko bang ipagpatuloy ang sarili kong endpoint o ibang form service?
Oo — huwag mo lang idagdag ang attribute. Ang mga form na walang harvis-form ay inihahain nang eksakto sa pagkasulat mo, pati na ang action. Sa form na mayroon nito, pinapalitan ng harvis ang action, dahil ang pagdaragdag ng attribute ang paraan mo para sabihin kung saan dapat pumunta ang submission.
Gumagana ba ito sa React, Vue o Svelte?
Oo, may isang dagdag na hakbang lang. Kung nasa HTML na ginagawa ng build mo ang form, sapat na ang attribute. Kung lumalabas lang ito matapos tumakbo ang JavaScript, isulat sa component ang action="/__harvis/form/contact" at method="post" — parehong endpoint, parehong dashboard, at ang browser pa rin ang nagpapadala.
Puwede bang mag-attach ng file ang mga tao?
Hindi pa. Gumagana pa rin sa bisita ang file input sa isang kinokolektang form, pero hindi iniimbak ang file — ang mga text field lang ang itinatago.
Ano ang mangyayari sa mga submission kapag na-delete ko ang site?
Kasama silang mawawala. Sa site nakakabit ang mga submission, hindi sa isang deploy, kaya nananatili sila kapag nag-deploy ka uli at tuluyang nawawala kapag na-delete ang site. Mag-export muna ng CSV kung gusto mo silang itago.
Mag-online ng site at subukan mo
Libre ang pag-publish at mga dalawang segundo lang. Idagdag ang attribute sa isang form, i-deploy uli, at padalhan ang sarili mo ng test message.