5 Professional HTML Form Designs with Apps Script to Connect with Google Sheets

Standard Google Forms are easy to build, but their default styling rarely fits the branding of a modern website or portfolio. Embedding an unstyled iframe can look clunky and reduce form completion rates.

By pairing custom HTML5 and CSS with a lightweight Google Apps Script backend, you can design high-converting, fully customized web forms that send submitted data directly into a Google Sheet—without requiring external database servers or paid third-party form plugins.

5 Professional HTML Form Designs with Apps Script to Connect with Google Sheets

The Universal Google Apps Script Backend

Before creating your frontend forms, set up the Google Apps Script Web App in your target Google Sheet to handle incoming submissions.

Step 1: Set Up the Script

  1. Open a new or existing Google Sheet.
  2. Rename the first tab to Submissions.
  3. Click Extensions > Apps Script.
  4. Paste the universal backend code below into the editor:
const SHEET_NAME = "Submissions";

function doPost(e) {
  const lock = LockService.getScriptLock();
  lock.tryLock(10000);

  try {
    const doc = SpreadsheetApp.getActiveSpreadsheet();
    let sheet = doc.getSheetByName(SHEET_NAME);
    
    if (!sheet) {
      sheet = doc.insertSheet(SHEET_NAME);
    }

    // Handles both FormData and JSON submissions reliably
    let data;
    if (e.postData && e.postData.type === "application/json") {
      data = JSON.parse(e.postData.contents);
    } else {
      data = e.parameter;
    }

    const headers = sheet.getRange(1, 1, 1, Math.max(sheet.getLastColumn(), 1)).getValues()[0];
    
    if (headers[0] === "") {
      headers[0] = "Timestamp";
      sheet.getRange(1, 1).setValue("Timestamp");
    }

    const row = [new Date()];

    for (let key in data) {
      let colIndex = headers.indexOf(key);
      if (colIndex === -1) {
        sheet.getRange(1, headers.length + 1).setValue(key);
        headers.push(key);
        colIndex = headers.length - 1;
      }
      row[colIndex] = data[key];
    }

    sheet.appendRow(row);

    return ContentService
      .createTextOutput(JSON.stringify({ result: "success" }))
      .setMimeType(ContentService.MimeType.JSON);

  } catch (error) {
    return ContentService
      .createTextOutput(JSON.stringify({ result: "error", error: error.message }))
      .setMimeType(ContentService.MimeType.JSON);
  } finally {
    lock.releaseLock();
  }
}

Step 2: Deploy as a Web App

  1. Click Deploy > New deployment in the top right corner.
  2. Click the gear icon next to "Select type" and choose Web app.
  3. Set Execute as: Me (your email).
  4. Set Who has access: Anyone.
  5. Click Deploy and copy the generated Web App URL. You will replace YOUR_DEPLOYED_WEB_APP_URL in each form with this URL.
Deploying Apps Script Web App with Public Access

Design 1: Floating Contact Card

A modern card layout with rounded corners, subtle drop shadows, and clean inputs for inquiries.

<form id="contactForm" style="max-width: 440px; margin: 20px auto; padding: 24px; background: #fff; border-radius: 10px; box-shadow: 0 4px 16px rgba(0,0,0,0.08); font-family: sans-serif;">
  <h3 style="margin: 0 0 16px 0; color: #202124;">Get in Touch</h3>
  
  <label style="display: block; font-size: 13px; font-weight: 600; margin-bottom: 5px;">Full Name</label>
  <input type="text" name="Name" required style="width: 100%; box-sizing: border-box; padding: 10px; border: 1px solid #dadce0; border-radius: 6px; margin-bottom: 12px;">

  <label style="display: block; font-size: 13px; font-weight: 600; margin-bottom: 5px;">Email Address</label>
  <input type="email" name="Email" required style="width: 100%; box-sizing: border-box; padding: 10px; border: 1px solid #dadce0; border-radius: 6px; margin-bottom: 12px;">

  <label style="display: block; font-size: 13px; font-weight: 600; margin-bottom: 5px;">Message</label>
  <textarea name="Message" rows="4" required style="width: 100%; box-sizing: border-box; padding: 10px; border: 1px solid #dadce0; border-radius: 6px; margin-bottom: 16px;"></textarea>

  <button type="submit" id="btn1" style="width: 100%; padding: 12px; background: #1a73e8; color: #fff; font-weight: bold; border: none; border-radius: 6px; cursor: pointer;">Send Message</button>
  <p id="msg1" style="font-size: 13px; text-align: center; margin-top: 10px; display: none;"></p>
</form>

<script>
  const scriptURL1 = "YOUR_DEPLOYED_WEB_APP_URL";
  const form1 = document.getElementById("contactForm");

  form1.addEventListener("submit", e => {
    e.preventDefault();
    const btn = document.getElementById("btn1");
    const msg = document.getElementById("msg1");
    btn.disabled = true;
    btn.innerText = "Sending...";

    fetch(scriptURL1, { method: "POST", body: new FormData(form1) })
      .then(() => {
        msg.style.display = "block";
        msg.style.color = "#137333";
        msg.innerText = "Message sent successfully!";
        form1.reset();
      })
      .catch(() => {
        msg.style.display = "block";
        msg.style.color = "#c5221f";
        msg.innerText = "Error sending message.";
      })
      .finally(() => {
        btn.disabled = false;
        btn.innerText = "Send Message";
      });
  });
</script>

Design 2: Single-Row Waitlist Bar

An ultra-compact horizontal signup bar designed for hero banners and website footers.

<form id="waitlistForm" style="display: flex; flex-wrap: wrap; gap: 8px; max-width: 480px; margin: 20px auto; font-family: sans-serif;">
  <input type="email" name="Waitlist_Email" placeholder="Enter your business email" required style="flex: 1; min-width: 220px; padding: 12px; border: 1px solid #dadce0; border-radius: 6px;">
  <button type="submit" id="btn2" style="padding: 12px 20px; background: #0d652d; color: #fff; font-weight: bold; border: none; border-radius: 6px; cursor: pointer;">Join Waitlist</button>
  <p id="msg2" style="width: 100%; font-size: 13px; margin: 6px 0 0 0; display: none;"></p>
</form>

<script>
  const scriptURL2 = "YOUR_DEPLOYED_WEB_APP_URL";
  const form2 = document.getElementById("waitlistForm");

  form2.addEventListener("submit", e => {
    e.preventDefault();
    const btn = document.getElementById("btn2");
    const msg = document.getElementById("msg2");
    btn.disabled = true;
    btn.innerText = "Subscribing...";

    fetch(scriptURL2, { method: "POST", body: new FormData(form2) })
      .then(() => {
        msg.style.display = "block";
        msg.style.color = "#137333";
        msg.innerText = "You're on the list!";
        form2.reset();
      })
      .catch(() => {
        msg.style.display = "block";
        msg.style.color = "#c5221f";
        msg.innerText = "Error subscribing.";
      })
      .finally(() => {
        btn.disabled = false;
        btn.innerText = "Join Waitlist";
      });
  });
</script>

Design 3: Two-Column Candidate Registration

A structured multi-field registration layout using CSS Grid to organize form inputs cleanly across screens.

<form id="appForm" style="max-width: 540px; margin: 20px auto; padding: 24px; background: #fafafa; border: 1px solid #e0e0e0; border-radius: 8px; font-family: sans-serif;">
  <h3 style="margin: 0 0 16px 0; color: #1a73e8;">Candidate Registration</h3>
  
  <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; margin-bottom: 12px;">
    <div>
      <label style="display:block; font-size: 12px; font-weight: 600; margin-bottom: 4px;">First Name</label>
      <input type="text" name="First_Name" required style="width: 100%; box-sizing: border-box; padding: 8px; border: 1px solid #ccc; border-radius: 4px;">
    </div>
    <div>
      <label style="display:block; font-size: 12px; font-weight: 600; margin-bottom: 4px;">Last Name</label>
      <input type="text" name="Last_Name" required style="width: 100%; box-sizing: border-box; padding: 8px; border: 1px solid #ccc; border-radius: 4px;">
    </div>
  </div>

  <div style="margin-bottom: 12px;">
    <label style="display:block; font-size: 12px; font-weight: 600; margin-bottom: 4px;">Applying For Position</label>
    <select name="Position" required style="width: 100%; box-sizing: border-box; padding: 8px; border: 1px solid #ccc; border-radius: 4px; background: #fff;">
      <option value="Frontend Developer">Frontend Developer</option>
      <option value="Data Analyst">Data Analyst</option>
      <option value="Product Designer">Product Designer</option>
    </select>
  </div>

  <div style="margin-bottom: 16px;">
    <label style="display:block; font-size: 12px; font-weight: 600; margin-bottom: 4px;">Portfolio / LinkedIn Link</label>
    <input type="url" name="Portfolio_URL" required style="width: 100%; box-sizing: border-box; padding: 8px; border: 1px solid #ccc; border-radius: 4px;">
  </div>

  <button type="submit" id="btn3" style="width: 100%; padding: 10px; background: #1a73e8; color: #fff; font-weight: bold; border: none; border-radius: 4px; cursor: pointer;">Submit Application</button>
  <p id="msg3" style="font-size: 13px; text-align: center; margin-top: 10px; display: none;"></p>
</form>

<script>
  const scriptURL3 = "YOUR_DEPLOYED_WEB_APP_URL";
  const form3 = document.getElementById("appForm");

  form3.addEventListener("submit", e => {
    e.preventDefault();
    const btn = document.getElementById("btn3");
    const msg = document.getElementById("msg3");
    btn.disabled = true;
    btn.innerText = "Submitting...";

    fetch(scriptURL3, { method: "POST", body: new FormData(form3) })
      .then(() => {
        msg.style.display = "block";
        msg.style.color = "#137333";
        msg.innerText = "Application submitted successfully!";
        form3.reset();
      })
      .catch(() => {
        msg.style.display = "block";
        msg.style.color = "#c5221f";
        msg.innerText = "Error submitting application.";
      })
      .finally(() => {
        btn.disabled = false;
        btn.innerText = "Submit Application";
      });
  });
</script>

Design 4: 5-Star Feedback & Review Box

A feedback capture card with interactive star/score radio selections for immediate Net Promoter Score (NPS) tracking.

<form id="ratingForm" style="max-width: 440px; margin: 20px auto; padding: 20px; background: #ffffff; border: 1px solid #e8eaed; border-radius: 10px; font-family: sans-serif;">
  <h4 style="margin: 0 0 12px 0;">Rate Your Experience</h4>
  
  <div style="display: flex; justify-content: space-between; margin-bottom: 14px; padding: 8px; background: #f8f9fa; border-radius: 6px;">
    <label><input type="radio" name="Rating" value="1" required> 1 ⭐</label>
    <label><input type="radio" name="Rating" value="2"> 2 ⭐</label>
    <label><input type="radio" name="Rating" value="3"> 3 ⭐</label>
    <label><input type="radio" name="Rating" value="4"> 4 ⭐</label>
    <label><input type="radio" name="Rating" value="5" checked> 5 ⭐</label>
  </div>

  <textarea name="Feedback" placeholder="What can we improve?" rows="3" style="width: 100%; box-sizing: border-box; padding: 8px; border: 1px solid #dadce0; border-radius: 4px; margin-bottom: 12px;"></textarea>
  <button type="submit" id="btn4" style="width: 100%; padding: 10px; background: #34a853; color: white; border: none; font-weight: bold; border-radius: 4px; cursor: pointer;">Submit Review</button>
  <p id="msg4" style="font-size: 13px; text-align: center; margin-top: 10px; display: none;"></p>
</form>

<script>
  const scriptURL4 = "YOUR_DEPLOYED_WEB_APP_URL";
  const form4 = document.getElementById("ratingForm");

  form4.addEventListener("submit", e => {
    e.preventDefault();
    const btn = document.getElementById("btn4");
    const msg = document.getElementById("msg4");
    btn.disabled = true;
    btn.innerText = "Submitting...";

    fetch(scriptURL4, { method: "POST", body: new FormData(form4) })
      .then(() => {
        msg.style.display = "block";
        msg.style.color = "#137333";
        msg.innerText = "Review submitted successfully!";
        form4.reset();
      })
      .catch(() => {
        msg.style.display = "block";
        msg.style.color = "#c5221f";
        msg.innerText = "Error submitting review.";
      })
      .finally(() => {
        btn.disabled = false;
        btn.innerText = "Submit Review";
      });
  });
</script>

Design 5: Helpdesk Support Ticket Form

A ticketing submission layout with issue priority selection, subject line, and detailed issue description.

<form id="ticketForm" style="max-width: 440px; margin: 20px auto; padding: 22px; background: #fff; border-top: 4px solid #ea4335; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.06); font-family: sans-serif;">
  <h3 style="margin: 0 0 14px 0; color: #202124;">Submit Support Ticket</h3>

  <label style="display:block; font-size: 12px; font-weight: 600; color: #666; margin-bottom: 4px;">Priority Level</label>
  <select name="Priority" style="width: 100%; box-sizing: border-box; padding: 8px; border: 1px solid #ccc; border-radius: 4px; margin-bottom: 12px; background: #fff;">
    <option value="Low">🟢 Low Priority</option>
    <option value="Medium" selected>🟡 Medium Priority</option>
    <option value="High">🔴 High / Urgent</option>
  </select>

  <label style="display:block; font-size: 12px; font-weight: 600; color: #666; margin-bottom: 4px;">Subject</label>
  <input type="text" name="Subject" required style="width: 100%; box-sizing: border-box; padding: 8px; border: 1px solid #ccc; border-radius: 4px; margin-bottom: 12px;">

  <label style="display:block; font-size: 12px; font-weight: 600; color: #666; margin-bottom: 4px;">Detailed Issue</label>
  <textarea name="Details" rows="3" required style="width: 100%; box-sizing: border-box; padding: 8px; border: 1px solid #ccc; border-radius: 4px; margin-bottom: 14px;"></textarea>

  <button type="submit" id="btn5" style="width: 100%; padding: 10px; background: #ea4335; color: white; border: none; font-weight: bold; border-radius: 4px; cursor: pointer;">Submit Ticket</button>
  <p id="msg5" style="font-size: 13px; text-align: center; margin-top: 10px; display: none;"></p>
</form>

<script>
  const scriptURL5 = "YOUR_DEPLOYED_WEB_APP_URL";
  const form5 = document.getElementById("ticketForm");

  form5.addEventListener("submit", e => {
    e.preventDefault();
    const btn = document.getElementById("btn5");
    const msg = document.getElementById("msg5");
    btn.disabled = true;
    btn.innerText = "Submitting...";

    fetch(scriptURL5, { method: "POST", body: new FormData(form5) })
      .then(() => {
        msg.style.display = "block";
        msg.style.color = "#137333";
        msg.innerText = "Ticket created successfully!";
        form5.reset();
      })
      .catch(() => {
        msg.style.display = "block";
        msg.style.color = "#c5221f";
        msg.innerText = "Error creating ticket.";
      })
      .finally(() => {
        btn.disabled = false;
        btn.innerText = "Submit Ticket";
      });
  });
</script>

Related Automation & Development Guides

Comments