★ Linklock.live Help ★
Your home on the World Wide Web
★ Help & Documentation
★ Google Sheets Integration
While Linklock is focused on static websites, we understand that some sites benefit from dynamic features like guestbooks, contact forms, or simple databases. Google Sheets combined with Google Apps Script provides a free, simple way to add these features while maintaining the spirit of the early web.
You Own Everything: This is a DIY setup guide. You create and control your own Google Sheet and Apps Script — Linklock never has access to your data.
📋 Step-by-Step Setup Guide
Step 1: Create a Google Sheet
- Go to sheets.google.com
- Click the + button to create a new blank spreadsheet
- Name it something like "My Website Data"
- In Row 1, add column headers:
timestamp,name,email,message(or whatever fields you need)
Step 2: Open Apps Script
- In your Google Sheet, click Extensions → Apps Script
- This opens the Apps Script editor in a new tab
- Delete any existing code in the editor
Step 3: Add the Script Code
Copy and paste this code into the Apps Script editor:
// Handle form submissions (POST requests)
function doPost(e) {
try {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var newRow = [];
// Add timestamp as first column
newRow.push(new Date().toISOString());
// Map form data to columns (skip timestamp column)
for (var i = 1; i < headers.length; i++) {
var header = headers[i].toString().toLowerCase();
newRow.push(e.parameter[header] || '');
}
// Append the new row
sheet.appendRow(newRow);
return ContentService
.createTextOutput(JSON.stringify({ result: 'success' }))
.setMimeType(ContentService.MimeType.JSON);
} catch (error) {
return ContentService
.createTextOutput(JSON.stringify({ result: 'error', error: error.toString() }))
.setMimeType(ContentService.MimeType.JSON);
}
}
// Handle read requests (GET requests)
function doGet(e) {
try {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = sheet.getDataRange().getValues();
var headers = data[0];
var result = [];
// Convert rows to objects (skip header row)
for (var i = 1; i < data.length; i++) {
var row = {};
for (var j = 0; j < headers.length; j++) {
row[headers[j]] = data[i][j];
}
result.push(row);
}
return ContentService
.createTextOutput(JSON.stringify(result))
.setMimeType(ContentService.MimeType.JSON);
} catch (error) {
return ContentService
.createTextOutput(JSON.stringify({ error: error.toString() }))
.setMimeType(ContentService.MimeType.JSON);
}
}Step 4: Deploy as Web App
- Click the Deploy button (top right) → New deployment
- Click the gear icon ⚙️ next to "Select type" and choose Web app
- Set these options:
- Description: "Form handler" (or anything you like)
- Execute as: Me
- Who has access: Anyone
- Click Deploy
- Click Authorize access and follow the prompts to grant permissions
- Copy the Web app URL — this is your API endpoint!
✓ Done!
Your endpoint URL will look like:https://script.google.com/macros/s/AKfycb...xyz/exec
Use this URL in your website forms (see sample code below).
📝 Sample Code: Contact Form
Add this to your HTML file. Replace YOUR_APPS_SCRIPT_URL with your actual endpoint URL from Step 4.
<form id="contactForm">
<input type="text" name="name" placeholder="Your Name" required>
<input type="email" name="email" placeholder="Your Email" required>
<textarea name="message" placeholder="Your Message" required></textarea>
<button type="submit">Send Message</button>
</form>
<script>
document.getElementById('contactForm').addEventListener('submit', function(e) {
e.preventDefault();
var form = this;
var submitBtn = form.querySelector('button[type="submit"]');
var originalText = submitBtn.textContent;
submitBtn.textContent = 'Sending...';
submitBtn.disabled = true;
fetch('YOUR_APPS_SCRIPT_URL', {
method: 'POST',
body: new FormData(form)
})
.then(function(response) { return response.json(); })
.then(function(data) {
if (data.result === 'success') {
alert('Thanks for your message!');
form.reset();
} else {
throw new Error(data.error || 'Unknown error');
}
})
.catch(function(error) {
console.error('Error:', error);
alert('Error sending message. Please try again.');
})
.finally(function() {
submitBtn.textContent = originalText;
submitBtn.disabled = false;
});
});
</script>📖 Sample Code: Guestbook
A guestbook that both saves entries AND displays existing ones. Make sure your Google Sheet has columns: timestamp, name, location, message
<h2>Sign My Guestbook!</h2>
<form id="guestbookForm">
<input type="text" name="name" placeholder="Your Name" required>
<input type="text" name="location" placeholder="Where are you from?">
<textarea name="message" placeholder="Leave a message" required></textarea>
<button type="submit">Sign Guestbook</button>
</form>
<h3>Previous Visitors</h3>
<div id="entries">Loading entries...</div>
<script>
var scriptURL = 'YOUR_APPS_SCRIPT_URL';
// Load existing entries on page load
function loadEntries() {
fetch(scriptURL)
.then(function(response) { return response.json(); })
.then(function(data) {
var entriesDiv = document.getElementById('entries');
entriesDiv.textContent = ''; // Clear safely
if (data.length === 0) {
entriesDiv.textContent = 'No entries yet. Be the first!';
return;
}
// Show entries in reverse order (newest first)
data.reverse().forEach(function(entry) {
var div = document.createElement('div');
div.style.borderBottom = '1px solid #ccc';
div.style.padding = '10px 0';
var header = document.createElement('strong');
header.textContent = entry.name;
if (entry.location) {
header.textContent += ' from ' + entry.location;
}
var message = document.createElement('p');
message.textContent = entry.message;
var date = document.createElement('small');
date.textContent = new Date(entry.timestamp).toLocaleDateString();
date.style.color = '#666';
div.appendChild(header);
div.appendChild(message);
div.appendChild(date);
entriesDiv.appendChild(div);
});
})
.catch(function(error) {
document.getElementById('entries').textContent = 'Error loading entries.';
});
}
// Submit new entry
document.getElementById('guestbookForm').addEventListener('submit', function(e) {
e.preventDefault();
var form = this;
var btn = form.querySelector('button');
btn.disabled = true;
btn.textContent = 'Signing...';
fetch(scriptURL, { method: 'POST', body: new FormData(form) })
.then(function(response) { return response.json(); })
.then(function(data) {
if (data.result === 'success') {
alert('Thanks for signing the guestbook!');
form.reset();
loadEntries(); // Reload to show new entry
}
})
.finally(function() {
btn.disabled = false;
btn.textContent = 'Sign Guestbook';
});
});
// Load entries when page loads
loadEntries();
</script>🔄 Updating Your Script
If you make changes to your Apps Script code, you need to create a new deployment:
- Click Deploy → Manage deployments
- Click the pencil icon ✏️ to edit your deployment
- Under "Version", select New version
- Click Deploy
Note: The URL stays the same, so you don't need to update your website code.
💡 Tips & Best Practices
Google Apps Script has rate limits (~20,000 calls/day for free accounts). Best for low-traffic features.
Always validate form input on the client side to reduce invalid submissions.
Regularly check your Google Sheet and clean up old or spam data.
Don't store sensitive information. This is not meant for passwords or payment data.
🔧 Troubleshooting
| CORS Error | Make sure your deployment is set to "Anyone" can access. If still failing, try using mode: 'no-cors' in your fetch options. |
| Script not updating | After editing code, you must create a new version via Deploy → Manage deployments. |
| Data not appearing | Check that your form field name attributes match your Google Sheet column headers exactly (case-insensitive). |
| Authorization error | Re-deploy your script and make sure to authorize all permissions when prompted. |
Need More Help?
If you have questions or need assistance with your Google Sheets integration, please reach out to us:
support@linklock.live
Best viewed with any browser • Made with ♥ for the small web