// architecture

HTML form vs JavaScript form — which should you use?

Every "JavaScript form" still renders HTML inputs — the difference is whether submission relies on the browser's native behavior or on fetch / client-side frameworks. Here is how to choose, with patterns that work on static sites and in React apps.

At a glance

Factor Native HTML form JavaScript-enhanced form
Works without JS Yes No (by definition)
Page reload on submit Usually yes No — stays on page
Browser validation required, type, pattern Custom + often duplicates HTML attrs
Accessibility Excellent baseline Requires manual ARIA care
Best backends Formspree, Basin, Netlify Same + JSON APIs, Formcarry
Bundle size Zero Framework + handler code

Native HTML form — the baseline

A plain HTML form uses the browser's built-in submission pipeline. Set action and method, add semantic labels, and the form works even if JavaScript fails to load — critical for accessibility and flaky mobile networks.

Pure HTML (no JavaScript)

<form action="https://formspree.io/f/xyz" method="POST">
  <input type="email" name="email" required />
  <button type="submit">Subscribe</button>
</form>

Use native HTML when: you ship static sites, care about Core Web Vitals, want maximum accessibility, or your users may block scripts.

JavaScript form — enhanced UX

Intercept submit, call preventDefault(), send data with fetch, then show inline success or error states. Formspree, Getform, and Formcarry all accept application/json POSTs for this pattern.

Progressive enhancement with fetch

const form = document.querySelector('#contact');
form.addEventListener('submit', async (e) => {
  e.preventDefault();
  const res = await fetch(form.action, {
    method: 'POST',
    body: new FormData(form),
    headers: { Accept: 'application/json' }
  });
  if (res.ok) form.replaceWith(successMessage);
});

Use JavaScript when: you are in a SPA (React, Vue, Svelte), need multi-step flows without full page loads, or want instant inline validation feedback.

Progressive enhancement (recommended)

Build the HTML form first so it works without scripts. Layer JavaScript to prevent reload and improve feedback. If the script errors, users can still submit — you do not lose leads.

  1. Write valid HTML with labels, required, and correct autocomplete
  2. Test submission with JavaScript disabled
  3. Add a submit handler that enhances, not replaces, the native flow
  4. Mirror server-side validation rules in client code when possible

Embed widgets: a third category

Form builders like forms.app and Jotform ship their own JavaScript. You paste embed code — neither pure HTML nor your custom JS. Trade-off: less control, faster setup, hosted validation and spam tools.

Rule of thumb: Static marketing site → native HTML + Formspree/Basin. React product app → controlled components + fetch. Non-technical team → no-code embed from forms.app.

Accessibility note

JavaScript forms often break accessibility when developers forget focus management, live regions for errors, and keyboard traps in custom widgets. Native HTML gives you submit-on-Enter, label association, and screen-reader announcements for free. If you go JS-heavy, test with VoiceOver or NVDA.

Related guides