How to Build a Simple CMS With Google Sheets (Manage Website Content Without a Dashboard)
Not every website needs WordPress. Not every project needs a headless CMS with a monthly bill. Sometimes you just need a simple way to let someone update website content without touching code.
Google Sheets can do that. And with Jsonsheets, your spreadsheet becomes a live API that feeds content directly to your website.
Here's how to build a lightweight CMS that anyone on your team can manage.
The Idea
Instead of hardcoding text, images, and page content into your website, you store everything in a Google Sheet. Your website fetches that content through an API at load time. When someone updates the spreadsheet, the website updates automatically.
No admin dashboard to build. No login system. No database migrations. Just a spreadsheet and an API.
Step 1: Structure Your Content in Google Sheets
Create a spreadsheet with separate tabs for different content types.
Tab: "pages"
| slug | title | description | hero_image | body |
| home | Welcome to Acme Co | We build tools for modern teams | https://example.com/hero.jpg | Acme Co helps startups ship faster... |
| about | About Us | Meet the team behind Acme | https://example.com/team.jpg | Founded in 2024, Acme Co started... |
| contact | Get In Touch | We'd love to hear from you | Email us at hello@acme.com or... |
Tab: "navigation"
| label | href | order | visible |
| Home | / | 1 | true |
| About | /about | 2 | true |
| Blog | /blog | 3 | true |
| Contact | /contact | 4 | true |
| Careers | /careers | 5 | false |
Tab: "settings"
| key | value |
| site_name | Acme Co |
| tagline | Tools for modern teams |
| logo_url | https://example.com/logo.png |
| primary_color | #4F46E5 |
| footer_text | 2026 Acme Co. All rights reserved. |
Each tab serves a different purpose, and each one gets its own API endpoint automatically.
Step 2: Connect to Jsonsheets
Sign in at jsonsheets.com
Browse your Drive and select the spreadsheet
Jsonsheets detects all three tabs and creates endpoints for each
Your endpoints:
GET /api/v1/acme-website/pages
GET /api/v1/acme-website/navigation
GET /api/v1/acme-website/settings
Step 3: Fetch Content in Your Website
Here's how to load page content dynamically.
Vanilla JavaScript:
const API_KEY = "js_your_api_key_here";
const BASE_URL = "https://jsonsheets.com/api/v1/acme-website";
async function fetchContent(tab, filters = "") {
const url = `${BASE_URL}/${tab}${filters ? "?" + filters : ""}`;
const res = await fetch(url, {
headers: { Authorization: `Bearer ${API_KEY}` }
});
const json = await res.json();
return json.data;
}
// Load a specific page by slug
async function loadPage(slug) {
const pages = await fetchContent("pages", `slug=${slug}`);
if (pages.length === 0) return null;
const page = pages[0];
document.title = page.title;
document.getElementById("page-title").textContent = page.title;
document.getElementById("page-body").innerHTML = page.body;
if (page.hero_image) {
document.getElementById("hero-img").src = page.hero_image;
}
}
// Load navigation
async function loadNav() {
const navItems = await fetchContent("navigation", "visible=true");
const nav = document.getElementById("main-nav");
navItems
.sort((a, b) => parseInt(a.order) - parseInt(b.order))
.forEach(item => {
const link = document.createElement("a");
link.href = item.href;
link.textContent = item.label;
nav.appendChild(link);
});
}
// Load site settings
async function loadSettings() {
const settings = await fetchContent("settings");
const config = {};
settings.forEach(row => { config[row.key] = row.value; });
document.getElementById("site-name").textContent = config.site_name;
document.getElementById("footer-text").textContent = config.footer_text;
}
// Initialize
loadNav();
loadSettings();
loadPage("home");
React version:
import { useState, useEffect } from "react";
const API_KEY = "js_your_api_key_here";
const BASE = "https://jsonsheets.com/api/v1/acme-website";
function useCMSContent(tab, filters) {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const params = filters ? "?" + new URLSearchParams(filters) : "";
fetch(`${BASE}/${tab}${params}`, {
headers: { Authorization: `Bearer ${API_KEY}` },
})
.then(res => res.json())
.then(json => setData(json.data))
.finally(() => setLoading(false));
}, [tab, JSON.stringify(filters)]);
return { data, loading };
}
function Page({ slug }) {
const { data, loading } = useCMSContent("pages", { slug });
const page = data[0];
if (loading) return <p>Loading...</p>;
if (!page) return <p>Page not found.</p>;
return (
<article>
{page.hero_image && <img src={page.hero_image} alt={page.title} />}
<h1>{page.title}</h1>
<p>{page.description}</p>
<div>{page.body}</div>
</article>
);
}
function Navigation() {
const { data } = useCMSContent("navigation", { visible: "true" });
return (
<nav>
{data
.sort((a, b) => a.order - b.order)
.map(item => (
<a key={item.href} href={item.href}>{item.label}</a>
))}
</nav>
);
}
Step 4: Let Your Team Manage Content
This is where the magic happens. Your content editors don't need to learn a CMS. They just open the Google Sheet and:
Edit text — Change any cell in the "pages" tab and the website updates
Add new pages — Add a row with a new slug, title, and body
Reorder navigation — Change the order numbers in the "navigation" tab
Hide pages — Set
visibletofalsein the navigation tabUpdate branding — Change colors, tagline, or logo in the "settings" tab
No deployments. No pull requests. No waiting for a developer.
Advanced: Multi-Language Support
Add a language column to your pages tab:
| slug | language | title | body |
| home | en | Welcome | Welcome to Acme... |
| home | es | Bienvenido | Bienvenido a Acme... |
| home | fr | Bienvenue | Bienvenue chez Acme... |
Then fetch by language:
GET /api/v1/acme-website/pages?slug=home&language=es
Your content team can manage translations side by side in the same spreadsheet.
Advanced: Blog Posts in a Spreadsheet
Add a "blog" tab:
| slug | title | author | published_date | category | excerpt | body | is_published |
| launch-day | We're Live | Sarah | 2026-01-15 | News | Today we launch... | Full article text here... | true |
| roadmap-2026 | Our 2026 Roadmap | Mike | 2026-02-01 | Product | Here's what's coming... | Full article text here... | true |
| draft-post | Upcoming Feature | Sarah | Product | Work in progress... | false |
Fetch published posts:
GET /api/v1/acme-website/blog?is_published=true
Draft posts stay invisible until someone changes is_published to true in the spreadsheet.
Caching for Performance
You might worry about loading speed if every page load hits an API. Jsonsheets handles this with built-in caching:
Server-side caching — Repeated requests are served from cache, not from Google's servers
ETag support — Your website can send conditional requests and get fast 304 responses when content hasn't changed
Configurable TTL — Control how often the cache refreshes based on how frequently your content changes
For a typical marketing site where content changes a few times a week, the cache keeps things fast without serving stale content.
When a Spreadsheet CMS Makes Sense
Great for:
Marketing sites and landing pages
Portfolio websites
Small business sites
Event pages and one-pagers
Client projects where the client manages their own content
MVPs and prototypes
Microsites and campaign pages
Consider a dedicated CMS when:
You need rich text editing with embedded media
Content has complex relationships (categories, tags, related posts)
You have dozens of content editors with different permissions
You need content versioning and approval workflows
The Advantages Over Traditional CMS Platforms
| Spreadsheet CMS | WordPress | Headless CMS | |
| Setup time | 5 minutes | 30+ minutes | 1+ hours |
| Learning curve | None (it's a spreadsheet) | Medium | High |
| Monthly cost | From free | Hosting + plugins | $29-$300+/month |
| Backend to maintain | None | PHP + MySQL | Depends |
| Non-technical editors | Easy | Medium | Hard |
| API included | Yes | Needs plugin | Yes |
Get Started
Building a spreadsheet-powered CMS takes less than 10 minutes:
Structure your content in Google Sheets
Connect the sheet at jsonsheets.com
Fetch content in your website via API
Hand the spreadsheet to your content team
No servers. No databases. No admin panels to build. Just a spreadsheet your team already knows how to use.
Build your spreadsheet CMS at jsonsheets.com