Skip to main content

Command Palette

Search for a command to run...

How to Build a Simple CMS With Google Sheets (Manage Website Content Without a Dashboard)

Updated
7 min read

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"

slugtitledescriptionhero_imagebody
homeWelcome to Acme CoWe build tools for modern teamshttps://example.com/hero.jpgAcme Co helps startups ship faster...
aboutAbout UsMeet the team behind Acmehttps://example.com/team.jpgFounded in 2024, Acme Co started...
contactGet In TouchWe'd love to hear from youEmail us at hello@acme.com or...

Tab: "navigation"

labelhrefordervisible
Home/1true
About/about2true
Blog/blog3true
Contact/contact4true
Careers/careers5false

Tab: "settings"

keyvalue
site_nameAcme Co
taglineTools for modern teams
logo_urlhttps://example.com/logo.png
primary_color#4F46E5
footer_text2026 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

  1. Sign in at jsonsheets.com

  2. Browse your Drive and select the spreadsheet

  3. 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 visible to false in the navigation tab

  • Update 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:

sluglanguagetitlebody
homeenWelcomeWelcome to Acme...
homeesBienvenidoBienvenido a Acme...
homefrBienvenueBienvenue 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:

slugtitleauthorpublished_datecategoryexcerptbodyis_published
launch-dayWe're LiveSarah2026-01-15NewsToday we launch...Full article text here...true
roadmap-2026Our 2026 RoadmapMike2026-02-01ProductHere's what's coming...Full article text here...true
draft-postUpcoming FeatureSarahProductWork 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 CMSWordPressHeadless CMS
Setup time5 minutes30+ minutes1+ hours
Learning curveNone (it's a spreadsheet)MediumHigh
Monthly costFrom freeHosting + plugins$29-$300+/month
Backend to maintainNonePHP + MySQLDepends
Non-technical editorsEasyMediumHard
API includedYesNeeds pluginYes

Get Started

Building a spreadsheet-powered CMS takes less than 10 minutes:

  1. Structure your content in Google Sheets

  2. Connect the sheet at jsonsheets.com

  3. Fetch content in your website via API

  4. 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