mga form

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.

index.html
<form harvis-form="contact">
  <input name="email" type="email" required>
  <textarea name="message"></textarea>
  <button>Send</button>
</form>
how it works

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.

  1. 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.

  2. 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.

  3. 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.

the markup

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.

ang sinusulat mo
<form harvis-form="contact"
      data-harvis-redirect="/thanks.html">
  <input name="email" type="email" required>
  <button>Send</button>
</form>
ang nakukuha ng mga bisita mo
<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.
frameworks

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
ContactForm.jsx
<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.

ai agents

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.

paste this into your ai agent
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.

included

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.

limits

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
faq

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.

Mag-online ng site