DEV Community

Cover image for How I let clients edit their Astro site without giving them WordPress back
Linc
Linc

Posted on

How I let clients edit their Astro site without giving them WordPress back

Every agency that moves clients off WordPress hits the same wall. Not speed, not hosting, not SEO. It's the client asking "ok but how do I change my opening hours now?"

On WordPress the answer was "log into wp-admin". Which also meant they could break the theme, install 4 plugins and ring you on a Friday. On a static Astro site the honest answer is usually "send me an email and I'll do it". That works for a while, then it turns into a stream of tiny unpaid jobs.

Here is how I handle it now after migrating a bunch of small business sites. Short version: don't give them a CMS. Give them the 3 or 4 things they actually change.

1. Figure out what they really edit

I went back through a year of client emails for about a dozen sites. Almost every request fell into a few buckets:

  • opening hours and holiday closures
  • a menu, price list or services list
  • team members (someone leaves, someone joins)
  • one "announcement" line at the top of the page

Nobody was asking to redesign the homepage. Nobody wanted to write blog posts (the ones who said they would, never did). So the whole "we need a CMS" conversation is mostly about 3 or 4 small lists.

2. Pull that content out into JSON

For each of those buckets I keep one file in public/data/:

public/
  data/
    hours.json
    menu.json
    team.json
Enter fullscreen mode Exit fullscreen mode

hours.json is boring on purpose:

{
  "monday": "7am - 3pm",
  "tuesday": "7am - 3pm",
  "saturday": "8am - 1pm",
  "sunday": "Closed",
  "note": "Closed 25-26 Dec"
}
Enter fullscreen mode Exit fullscreen mode

Everything else on the site stays as normal Astro components and markdown. The design, layout and copy are mine. The lists are theirs.

3. Fetch it at runtime, not build time

This is the bit that tripped me up first. In Astro it's tempting to import hours from '../data/hours.json' and render it at build time. That's fine until the client edits the file live, because the built HTML still has the old hours baked in.

So for client editable blocks I render a small fallback in the HTML and then fetch the JSON on the page:

<ul data-hours>
  <li>See our hours below</li>
</ul>
<script>
  fetch('/data/hours.json')
    .then((r) => r.json())
    .then((h) => {
      const el = document.querySelector('[data-hours]');
      el.innerHTML = ['monday','tuesday','saturday','sunday']
        .map((d) => `<li>${d}: ${h[d]}</li>`)
        .join('') + (h.note ? `<li>${h.note}</li>` : '');
    });
</script>
Enter fullscreen mode Exit fullscreen mode

Tiny, no framework island needed, and the page still loads fast. If you care about the hours being in the initial HTML for SEO, render the build time version as the fallback and let the fetch overwrite it. Best of both.

4. Give the client a form, not a file

The client is never going to open a JSON file. And I'm not giving them a git login.

I host these sites on novence.ai and the paid plans have an owner edit option: you set which email addresses can edit and which data files they can touch, and the client just bookmarks theirsite.com/edit. They type their email, get a one time code, and get a generated form for those fields. No password to forget, no admin panel, no plugin updates. The /edit page is noindex so it doesnt show up in Google.

For the restaurant clients this alone killed most of the "can you update the menu" emails.

If you're on another host the same idea works with any headless form + a small serverless function that writes the JSON. It's just more pieces to babysit.

5. Don't let your next deploy wipe their edits

This is the gotcha that will bite you once. The client edits menu.json live. A week later you push a design tweak from your repo, which still has the old menu.json. Congrats, you just reverted their menu.

What I do now:

  • before any deploy, pull the live /data/*.json files back into the repo
  • my coding agent does this as a step in the deploy script so I can't forget
  • the JSON is committed, so I also get a history of what the client changed and when

Something like:

for f in hours menu team; do
  curl -s "https://clientsite.com/data/$f.json" -o "public/data/$f.json"
done
git commit -am "sync client data" || true
npm run build && npx novence deploy ./dist
Enter fullscreen mode Exit fullscreen mode

6. What this doesn't do

Be upfront with clients about the edges:

  • it's not a page builder. New pages or layout changes are still a job for you (and billable)
  • don't put anything private in /data, those files are public URLs
  • bookings, payments and shop stuff go in a proper widget (Cal.com, Stripe payment links etc), not JSON

Most small business owners are actually relieved by this. A form with 6 fields is less scary than wp-admin ever was.

The result

Fewer tiny support jobs, clients who feel in control of the stuff they care about, and no WordPress admin to keep patched. When I pitch a WordPress to Astro move now, "you can still edit your hours and menu yourself" is usually the line that gets the yes.

If you've got a different setup for client edits on static sites I'd genuinly like to hear it in the comments.

Top comments (1)

Collapse
 
rushdeploy profile image
Rishabh Mishra •

The pull-live-JSON-before-deploy step is the one I would keep. It stops a routine design push from quietly reverting a client's menu or hours. I would also keep a short note in the form about what not to put in those files, since they are public URLs.