Why we replaced Sanity with JSON for our own site
We dropped a CMS we already had. The site got smaller, faster, and cheaper to think about. Here is the cost-of-clever math.
Our own site, maram.ch, used to run on Next.js + Sanity. We just redesigned it. The new version is Astro static SSG, and Sanity is gone.
Sanity is a great CMS. We did not drop it because it failed us. We dropped it because once we sat down to actually count the moving parts, we could not keep justifying them. Here is the math.
What we had
The pre-redesign stack: Next.js 16 (App Router, React 19), Tailwind v4, styled-components, GSAP + Framer Motion for animation, Three.js for a hero scene, the Sanity client for runtime fetches, an embedded Sanity Studio at /studio, Resend for the contact form, and the usual Vercel-shaped Dockerfile.
Production traffic to that site is the trickle you would expect for a small-studio landing page. Bounce rate aside, here is what each request actually paid for:
- A React tree rendered server-side, hydrated client-side
- A Sanity-client bundle in the page JS so any future drafts could preview live
- Three.js + R3F shaders for a hero animation that nobody asked for
- GSAP timelines whose scroll triggers attached on mount
Total page weight on the home: well over a megabyte before content. Time-to-interactive scaled with that.
What we kept asking ourselves
The redesign was driven by a positioning shift, not a performance complaint. But once we started rewriting the home page, three questions kept surfacing:
Do we have an editorial team? No. Just two founders, both of whom commit straight to Git when they want to change a sentence.
Do we need draft previews? No. We have never used the Sanity preview surface — drafts go straight to commit and through CI.
Do we need image transforms? Less than we thought. The team photos are 96×96 avatars; the OG image is hand-authored SVG. Astro’s native <Image> handles the rest at build time.
When three “do we need…” questions all answer “no” in a row, you are paying for capability you do not use.
What we replaced it with
A data/ folder. That is the whole replacement.
data/
README.md ← explains the two layers
site.json ← studio config (name, contact, social, SEO)
home.json ← home-page content
team.json ← founders
testimonials.json
partnerships.json
expertise/ ← one JSON per entry (4)
sectors/ ← one JSON per entry (4)
blog/ ← one markdown file per post (you are reading one)
images/ ← assets actually used by the site
sanity-export/ ← the lossless export, kept as an archive
Two layers — curated and raw. The curated layer at the top of data/ is what the Astro build reads at compile time. The raw layer underneath is the full lossless export of the original Sanity dataset, kept as a “we did not lose anything” guarantee.
The migration was a single TypeScript script (scripts/sanity-export.ts) that:
- Pulled every document of every type from Sanity using the existing client
- Walked every JSON for
image/fileasset references and downloaded the binaries todata/sanity-export/assets/ - Converted blog-post Portable Text bodies to Markdown with YAML frontmatter — image refs rewritten to local paths
- Wrote singletons as flat JSON, collections as one JSON per entry
- Generated a
manifest.jsonwith counts so a future re-export is diffable
The script is idempotent and still in the repo. If Sanity ever needs to be the source again, we re-run it and the curated files are untouched.
What the new site reads, in practice
Inside an Astro page, content is just a typed import:
import site from '../../data/site.json';
import home from '../../data/home.json';
import { getCollection } from 'astro:content';
const expertise = await getCollection('expertise');
Astro’s content collections give us a Zod schema and typed access. Markdown posts have <Content /> renderers. No client-side runtime, no network calls at request time, no client bundle bloat from a CMS SDK.
The numbers we got back
The new build produces a dist/ of about 48 KB for the home page. The only JavaScript on the page is Astro’s tiny <ClientRouter /> for view-transitions — roughly 16 KB. No React, no animation library, no Three.js, no Sanity SDK in the runtime bundle.
The container image dropped from “Next.js standalone build” (around 150 MB content) to nginxinc/nginx-unprivileged:alpine serving the static dist/ — about 21 MB content / 75 MB on disk. The chart no longer needs the six env-Secret entries that bound RESEND_API_KEY, CONTACT_EMAIL_*, NEXT_PUBLIC_SANITY_*, and SANITY_API_READ_TOKEN. The Secret comes out of the cluster entirely; the playbook drops the env task; we retired six vault keys.
pnpm install went from 33 dependencies (with peer-dep conflicts that needed --legacy-peer-deps) to 7. The first build was 2.3 seconds. Cold deploy is well under a minute.
What we lost
Honest list:
- A real WYSIWYG editor for non-technical editors. (Nobody on the team is one, so this hurt zero.)
- The ability to schedule a post for later. (Git scheduling via
atwould solve this, but again — not a real need.) - Live preview when authoring drafts. (Was nice in theory; in practice we always committed the draft to a branch and looked at the deploy preview.)
- Built-in image transforms. (Astro’s
<Image>handles this at build time. The cost is one extra build, not a separate service.)
If you employ writers who do not commit to Git, keep the CMS. The math flips immediately when a non-technical editor is in the loop. Sanity is excellent for that.
For our actual situation — two founders, infrequent edits, content that survives version control naturally — a CMS was an extra dependency we kept paying for in build weight, JavaScript footprint, runtime calls, and mental overhead.
The general lesson
Software stacks have a tendency to accrete. Every dependency was added with a justification. The justifications can survive their reasons for years. Sometimes the most useful thing you can do is sit down with the dependency list and ask “does this still earn its place?” for each item.
We did that exercise and removed 26 of our 33 dependencies. The site is faster, cheaper to run, and easier to reason about. None of our visitors lost anything.
If you suspect your stack has the same problem, we are an email away.