Tutorials Logic, IN info@tutorialslogic.com

AJAX Forms Submit Without Page Reload

Asynchronous Form Contract

An AJAX form must preserve the semantics and accessibility of an ordinary form: labeled controls, keyboard submission, validation feedback, and a clear result. Listen to submit rather than only button clicks, prevent default after deciding to handle the request, and allow one active submission at a time.

Submitting Forms with AJAX

By default, submitting an HTML form causes a full page reload. Using AJAX, you can intercept the submit event with preventDefault(), collect the form data, and send it to the server in the background - giving users instant feedback without a page reload.

Basic AJAX Form Submission

Basic AJAX Form Submission
document.getElementById('contact-form').addEventListener('submit', async function (e) {
  e.preventDefault(); // stop the default page reload

  const form = e.target;
  const submitBtn = form.querySelector('button[type="submit"]');
  const statusEl = document.getElementById('form-status');

  // Show loading state
  submitBtn.disabled = true;
  submitBtn.textContent = 'Sending...';
  statusEl.textContent = '';

  // Collect form data as a plain object
  const data = {
    name: form.name.value.trim(),
    email: form.email.value.trim(),
    message: form.message.value.trim()
  };

  // Basic client-side validation
  if (!data.name || !data.email || !data.message) {
    statusEl.textContent = 'Please fill in all fields.';
    statusEl.className = 'text-danger';
    submitBtn.disabled = false;
    submitBtn.textContent = 'Send';
    return;
  }

  try {
    const res = await fetch('/api/contact', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    });

    if (!res.ok) throw new Error(`Server error: ${res.status}`);

    statusEl.textContent = 'Message sent successfully!';
    statusEl.className = 'text-success';
    form.reset();
  } catch (err) {
    statusEl.textContent = `Failed to send: ${err.message}`;
    statusEl.className = 'text-danger';
  } finally {
    submitBtn.disabled = false;
    submitBtn.textContent = 'Send';
  }
});

FormData Object

The FormData API automatically serializes all form fields - including file inputs - into a format suitable for sending as a multipart request. You do not need to set Content-Type manually when using FormData with fetch(); the browser sets it automatically with the correct boundary.

FormData - Including File Uploads

FormData - Including File Uploads
document.getElementById('upload-form').addEventListener('submit', async function (e) {
  e.preventDefault();

  // FormData automatically captures all fields including file inputs
  const formData = new FormData(e.target);

  // You can also append extra fields manually
  formData.append('uploadedAt', new Date().toISOString());

  // Inspect FormData entries (for debugging)
  for (const [key, value] of formData.entries()) {
    console.log(key, value);
  }

  const progressBar = document.getElementById('upload-progress');

  try {
    // DO NOT set Content-Type header - browser sets it with boundary automatically
    const res = await fetch('/api/upload', {
      method: 'POST',
      body: formData
    });

    if (!res.ok) throw new Error(`Upload failed: ${res.status}`);

    const result = await res.json();
    console.log('Uploaded file URL:', result.url);
    document.getElementById('upload-status').textContent = 'Upload complete!';
  } catch (err) {
    console.error(err);
    document.getElementById('upload-status').textContent = err.message;
  }
});

Real-Time Form Validation with AJAX

Real-Time Form Validation with AJAX
// Check if username is available as the user types
const usernameInput = document.getElementById('username');
const usernameStatus = document.getElementById('username-status');

let debounceTimer;

usernameInput.addEventListener('input', function () {
  const username = this.value.trim();

  clearTimeout(debounceTimer); // reset timer on each keystroke

  if (username.length < 3) {
    usernameStatus.textContent = 'Username must be at least 3 characters.';
    usernameStatus.className = 'text-warning';
    return;
  }

  usernameStatus.textContent = 'Checking...';

  // Debounce: wait 400ms after user stops typing before sending request
  debounceTimer = setTimeout(async () => {
    try {
      const res = await fetch(`/api/check-username?username=${encodeURIComponent(username)}`);
      const data = await res.json();

      if (data.available) {
        usernameStatus.textContent = 'OK Username is available';
        usernameStatus.className = 'text-success';
      } else {
        usernameStatus.textContent = '✗ Username is already taken';
        usernameStatus.className = 'text-danger';
      }
    } catch {
      usernameStatus.textContent = 'Could not check availability';
      usernameStatus.className = 'text-muted';
    }
  }, 400);
});

FormData and JSON

Use FormData when sending files or the form’s native name/value representation; do not set its Content-Type manually because the browser supplies the multipart boundary. Use JSON when the API defines a JSON document and transform values deliberately, including checkboxes and repeated fields.

Keep entered values after a server validation failure and map structured errors to controls. On success, use the server-returned record and redirect only when the workflow needs a new location. Preserve a working non-JavaScript action when progressive enhancement is a product requirement.

Before you move on

AJAX Forms Submit Without Page Reload Mastery Check

5 checks
  • By default, submitting an HTML form causes a full page reload.
  • Using AJAX, you can intercept the submit event with preventDefault(), collect the form data, and send it to the server in the background - giving users instant feedback without a page reload.
  • The FormData API automatically serializes all form fields - including file inputs - into a format suitable for sending as a multipart request.
  • You do not need to set Content-Type manually when using FormData with fetch(); the browser sets it automatically with the correct boundary.
  • This prevents the lesson from becoming a list of commands with no practical meaning.
Next Step
Next Practice

Finish the concept here, then reinforce it with hands-on coding, interview prep, or a tool that matches the topic.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.