form-to-google-sheet 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Code.gs ADDED
@@ -0,0 +1,56 @@
1
+ // ===========================================================
2
+ // Google Apps Script — paste this into your Sheet's script editor
3
+ // (Extensions > Apps Script), then deploy as a Web App.
4
+ // ===========================================================
5
+
6
+ var SHEET_NAME = "Sheet1"; // change if your tab has a different name
7
+
8
+ function doPost(e) {
9
+ try {
10
+ var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_NAME);
11
+ if (!sheet) {
12
+ return _jsonResponse(404, { error: "Sheet '" + SHEET_NAME + "' not found" });
13
+ }
14
+
15
+ var data = JSON.parse(e.postData.contents);
16
+ var rows = data.rows || [data]; // accept a single object or { rows: [...] }
17
+
18
+ var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0].filter(String);
19
+
20
+ if (headers.length === 0) {
21
+ // Sheet is empty — use the keys from the first row as headers
22
+ headers = Object.keys(rows[0]);
23
+ sheet.getRange(1, 1, 1, headers.length).setValues([headers]);
24
+ }
25
+
26
+ var output = [];
27
+ rows.forEach(function (row) {
28
+ var newRow = headers.map(function (header) {
29
+ return row[header] !== undefined ? row[header] : "";
30
+ });
31
+ sheet.appendRow(newRow);
32
+ output.push(newRow);
33
+ });
34
+
35
+ return _jsonResponse(200, {
36
+ status: "ok",
37
+ rowsAdded: output.length,
38
+ headers: headers,
39
+ });
40
+ } catch (err) {
41
+ return _jsonResponse(500, { error: err.message });
42
+ }
43
+ }
44
+
45
+ function doGet() {
46
+ return _jsonResponse(200, {
47
+ status: "ok",
48
+ message: "FormToGoogleSheet endpoint is live. Send a POST to add rows.",
49
+ });
50
+ }
51
+
52
+ function _jsonResponse(code, payload) {
53
+ return ContentService.createTextOutput(JSON.stringify(payload)).setMimeType(
54
+ ContentService.MimeType.JSON
55
+ );
56
+ }
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sahil
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,174 @@
1
+ # FormToGoogleSheet
2
+
3
+ Submit HTML form data straight to a Google Sheet. No API keys, no server, no database — just a Google Sheet and a `<script>` tag.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install form-to-google-sheet
9
+ ```
10
+
11
+ Or use a CDN — no install needed:
12
+
13
+ ```html
14
+ <script src="https://unpkg.com/form-to-google-sheet"></script>
15
+ ```
16
+
17
+ Or with jsdelivr:
18
+
19
+ ```html
20
+ <script src="https://cdn.jsdelivr.net/npm/form-to-google-sheet"></script>
21
+ ```
22
+
23
+ ## How It Works
24
+
25
+ ```
26
+ Browser form ──POST──▶ Google Apps Script (free) ──▶ Google Sheet
27
+ ```
28
+
29
+ Google Apps Script acts as a tiny serverless endpoint that lives inside your spreadsheet. You deploy it once, get a URL, and POST to it from anywhere.
30
+
31
+ ---
32
+
33
+ ## Setup (5 minutes)
34
+
35
+ ### 1. Prepare your Google Sheet
36
+
37
+ Open your sheet and add **column headers** in Row 1.
38
+ The header names must match the keys you send from your form.
39
+
40
+ | name | email | message |
41
+ |------|-------|---------|
42
+ | | | |
43
+
44
+ > If the sheet is empty (no headers), the script will auto-create headers from the first submission's keys.
45
+
46
+ ### 2. Add the Apps Script
47
+
48
+ 1. In your Google Sheet go to **Extensions → Apps Script**.
49
+ 2. Delete any code in the editor and paste the contents of [`Code.gs`](Code.gs).
50
+ 3. If your tab is not called `Sheet1`, update the `SHEET_NAME` variable at the top.
51
+ 4. Click **💾 Save**.
52
+
53
+ ### 3. Deploy as a Web App
54
+
55
+ 1. In the Apps Script editor click **Deploy → New deployment**.
56
+ 2. Click the gear icon next to **Select type** and choose **Web app**.
57
+ 3. Set:
58
+ - **Description**: anything you like
59
+ - **Execute as**: **Me**
60
+ - **Who has access**: **Anyone**
61
+ 4. Click **Deploy**.
62
+ 5. Authorize the script when prompted (it needs permission to edit *your* sheet).
63
+ 6. Copy the **Web app URL** — it looks like:
64
+ `https://script.google.com/macros/s/AKfycb.../exec`
65
+
66
+ ### 4. Add to Your Project
67
+
68
+ **With npm / a bundler (React, Next.js, Vite, etc.):**
69
+
70
+ ```js
71
+ import FormToSheet from "form-to-google-sheet";
72
+
73
+ FormToSheet.init("https://script.google.com/macros/s/YOUR_ID/exec");
74
+ ```
75
+
76
+ **With a CDN / script tag (plain HTML):**
77
+
78
+ ```html
79
+ <script src="https://unpkg.com/form-to-google-sheet"></script>
80
+ <script>
81
+ FormToSheet.init("https://script.google.com/macros/s/YOUR_ID/exec");
82
+ </script>
83
+ ```
84
+
85
+ ---
86
+
87
+ ## Usage
88
+
89
+ ### Option A: Bind to a `<form>`
90
+
91
+ ```html
92
+ <form id="contact-form">
93
+ <input name="name" required />
94
+ <input name="email" required />
95
+ <textarea name="message"></textarea>
96
+ <button type="submit">Send</button>
97
+ </form>
98
+
99
+ <script src="form-to-sheet.js"></script>
100
+ <script>
101
+ FormToSheet.init("YOUR_APPS_SCRIPT_URL");
102
+ FormToSheet.bind("#contact-form", {
103
+ onSuccess: () => alert("Saved!"),
104
+ onError: (err) => console.error(err),
105
+ resetOnSuccess: true, // default: true
106
+ });
107
+ </script>
108
+ ```
109
+
110
+ ### Option B: Submit Programmatically
111
+
112
+ ```js
113
+ FormToSheet.init("YOUR_APPS_SCRIPT_URL");
114
+
115
+ // single row
116
+ FormToSheet.submit({ name: "Ada", email: "ada@example.com" })
117
+ .then(console.log);
118
+
119
+ // multiple rows at once
120
+ FormToSheet.submit([
121
+ { name: "Ada", email: "ada@example.com" },
122
+ { name: "Grace", email: "grace@example.com" },
123
+ ]);
124
+ ```
125
+
126
+ ### Option C: Use in React
127
+
128
+ ```jsx
129
+ import { useRef } from "react";
130
+ import FormToSheet from "form-to-google-sheet";
131
+
132
+ FormToSheet.init("YOUR_APPS_SCRIPT_URL");
133
+
134
+ export default function ContactForm() {
135
+ const formRef = useRef(null);
136
+
137
+ const handleSubmit = async (e) => {
138
+ e.preventDefault();
139
+ const data = Object.fromEntries(new FormData(formRef.current));
140
+ await FormToSheet.submit(data);
141
+ alert("Done!");
142
+ formRef.current.reset();
143
+ };
144
+
145
+ return (
146
+ <form ref={formRef} onSubmit={handleSubmit}>
147
+ <input name="name" required />
148
+ <input name="email" required />
149
+ <button type="submit">Send</button>
150
+ </form>
151
+ );
152
+ }
153
+ ```
154
+
155
+ Tip: call `FormToSheet.init(url)` once at app startup (e.g. in `main.jsx`) instead of per-component if you prefer.
156
+
157
+ ---
158
+
159
+ ## API
160
+
161
+ | Method | Description |
162
+ |--------|-------------|
163
+ | `FormToSheet.init(url)` | Set the Apps Script deployment URL. Call once. |
164
+ | `FormToSheet.submit(data)` | Send a row (`{}`) or rows (`[{}, ...]`). Returns a `Promise`. |
165
+ | `FormToSheet.bind(selector, opts)` | Auto-wire a `<form>` element. Options: `onSuccess`, `onError`, `resetOnSuccess`. |
166
+
167
+ ---
168
+
169
+ ## Notes
170
+
171
+ - **No API key required.** The Google Apps Script web app handles auth.
172
+ - **CORS**: The library uses `mode: "no-cors"` so it works from any origin. The trade-off is you can't read the response body — but the row still gets written. If the fetch doesn't throw, it worked.
173
+ - **Rate limits**: Google Apps Script allows ~20,000 calls/day on a free Google account. More than enough for contact forms and small apps.
174
+ - **Security**: Anyone with the URL can POST to your sheet. For production, consider adding a shared secret or honeypot field in the Apps Script.
@@ -0,0 +1,106 @@
1
+ /**
2
+ *
3
+ * Sahil Bambulkar (https://www.linkedin.com/in/sahil-bambulkar/)
4
+ * FormToGoogleSheet — tiny client library (no dependencies, ~1 KB)
5
+ *
6
+ * Usage (script tag):
7
+ * <script src="form-to-sheet.js"></script>
8
+ * <script>
9
+ * FormToSheet.init("https://script.google.com/macros/s/YOUR_DEPLOY_ID/exec");
10
+ * FormToSheet.submit({ name: "Ada", email: "ada@example.com" });
11
+ * </script>
12
+ *
13
+ * Usage (ES module):
14
+ * import { init, submit } from "./form-to-sheet.js";
15
+ * init("https://script.google.com/macros/s/YOUR_DEPLOY_ID/exec");
16
+ * submit({ name: "Ada", email: "ada@example.com" });
17
+ */
18
+ (function (root, factory) {
19
+ if (typeof module === "object" && module.exports) {
20
+ module.exports = factory();
21
+ } else if (typeof define === "function" && define.amd) {
22
+ define(factory);
23
+ } else {
24
+ root.FormToSheet = factory();
25
+ }
26
+ })(typeof self !== "undefined" ? self : this, function () {
27
+ "use strict";
28
+
29
+ var _url = "";
30
+
31
+ function init(scriptUrl) {
32
+ if (!scriptUrl) throw new Error("[FormToSheet] Missing Apps Script URL");
33
+ _url = scriptUrl.replace(/\/+$/, "");
34
+ }
35
+
36
+ function _ensureInit() {
37
+ if (!_url)
38
+ throw new Error(
39
+ "[FormToSheet] Call FormToSheet.init(url) before submitting"
40
+ );
41
+ }
42
+
43
+ /**
44
+ * Submit a single row (object) or multiple rows (array of objects).
45
+ * Returns a Promise that resolves with the Apps Script JSON response.
46
+ */
47
+ function submit(data) {
48
+ _ensureInit();
49
+ var body = Array.isArray(data) ? { rows: data } : data;
50
+
51
+ return fetch(_url, {
52
+ method: "POST",
53
+ mode: "no-cors",
54
+ headers: { "Content-Type": "text/plain" },
55
+ body: JSON.stringify(body),
56
+ }).then(function (res) {
57
+ // Google Apps Script redirects and returns opaque responses in no-cors
58
+ // mode, so we can't reliably read the body. If the fetch didn't throw,
59
+ // the request was accepted.
60
+ if (res.type === "opaque") {
61
+ return { status: "ok", note: "opaque response (no-cors mode)" };
62
+ }
63
+ return res.json();
64
+ });
65
+ }
66
+
67
+ /**
68
+ * Convenience: bind to a <form> element. On submit it reads all named
69
+ * inputs, sends them to the sheet, and calls your callback.
70
+ *
71
+ * FormToSheet.bind("#my-form", {
72
+ * onSuccess: () => alert("Saved!"),
73
+ * onError: (err) => console.error(err),
74
+ * });
75
+ */
76
+ function bind(selectorOrEl, opts) {
77
+ _ensureInit();
78
+ opts = opts || {};
79
+ var form =
80
+ typeof selectorOrEl === "string"
81
+ ? document.querySelector(selectorOrEl)
82
+ : selectorOrEl;
83
+
84
+ if (!form) throw new Error("[FormToSheet] Form not found: " + selectorOrEl);
85
+
86
+ form.addEventListener("submit", function (e) {
87
+ e.preventDefault();
88
+ var formData = new FormData(form);
89
+ var row = {};
90
+ formData.forEach(function (value, key) {
91
+ row[key] = value;
92
+ });
93
+
94
+ submit(row)
95
+ .then(function (res) {
96
+ if (opts.onSuccess) opts.onSuccess(res);
97
+ if (opts.resetOnSuccess !== false) form.reset();
98
+ })
99
+ .catch(function (err) {
100
+ if (opts.onError) opts.onError(err);
101
+ });
102
+ });
103
+ }
104
+
105
+ return { init: init, submit: submit, bind: bind };
106
+ });
package/index.mjs ADDED
@@ -0,0 +1,59 @@
1
+ let _url = "";
2
+
3
+ function init(scriptUrl) {
4
+ if (!scriptUrl) throw new Error("[FormToSheet] Missing Apps Script URL");
5
+ _url = scriptUrl.replace(/\/+$/, "");
6
+ }
7
+
8
+ function _ensureInit() {
9
+ if (!_url)
10
+ throw new Error(
11
+ "[FormToSheet] Call FormToSheet.init(url) before submitting"
12
+ );
13
+ }
14
+
15
+ export function submit(data) {
16
+ _ensureInit();
17
+ const body = Array.isArray(data) ? { rows: data } : data;
18
+
19
+ return fetch(_url, {
20
+ method: "POST",
21
+ mode: "no-cors",
22
+ headers: { "Content-Type": "text/plain" },
23
+ body: JSON.stringify(body),
24
+ }).then((res) => {
25
+ if (res.type === "opaque") {
26
+ return { status: "ok", note: "opaque response (no-cors mode)" };
27
+ }
28
+ return res.json();
29
+ });
30
+ }
31
+
32
+ export function bind(selectorOrEl, opts = {}) {
33
+ _ensureInit();
34
+ const form =
35
+ typeof selectorOrEl === "string"
36
+ ? document.querySelector(selectorOrEl)
37
+ : selectorOrEl;
38
+
39
+ if (!form)
40
+ throw new Error("[FormToSheet] Form not found: " + selectorOrEl);
41
+
42
+ form.addEventListener("submit", (e) => {
43
+ e.preventDefault();
44
+ const formData = new FormData(form);
45
+ const row = Object.fromEntries(formData);
46
+
47
+ submit(row)
48
+ .then((res) => {
49
+ if (opts.onSuccess) opts.onSuccess(res);
50
+ if (opts.resetOnSuccess !== false) form.reset();
51
+ })
52
+ .catch((err) => {
53
+ if (opts.onError) opts.onError(err);
54
+ });
55
+ });
56
+ }
57
+
58
+ export { init };
59
+ export default { init, submit, bind };
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "form-to-google-sheet",
3
+ "version": "1.0.0",
4
+ "description": "Submit HTML form data straight to a Google Sheet. No API keys, no server, no database.",
5
+ "main": "form-to-sheet.js",
6
+ "module": "index.mjs",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./index.mjs",
10
+ "require": "./form-to-sheet.js",
11
+ "default": "./form-to-sheet.js"
12
+ }
13
+ },
14
+ "browser": "form-to-sheet.js",
15
+ "unpkg": "form-to-sheet.js",
16
+ "jsdelivr": "form-to-sheet.js",
17
+ "files": [
18
+ "form-to-sheet.js",
19
+ "index.mjs",
20
+ "Code.gs",
21
+ "README.md",
22
+ "LICENSE"
23
+ ],
24
+ "scripts": {
25
+ "test": "echo \"No tests yet\" && exit 0"
26
+ },
27
+ "keywords": [
28
+ "google-sheets",
29
+ "google-sheet",
30
+ "form",
31
+ "form-submit",
32
+ "spreadsheet",
33
+ "google-apps-script",
34
+ "no-server",
35
+ "serverless"
36
+ ],
37
+ "author": "Sahil",
38
+ "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/sahil/form-to-google-sheet.git"
42
+ },
43
+ "homepage": "https://github.com/sahil/form-to-google-sheet#readme",
44
+ "bugs": {
45
+ "url": "https://github.com/sahil/form-to-google-sheet/issues"
46
+ }
47
+ }