Find a Stay is live. Search pet friendly accommodation across Australia. Try it now →
Founder StoryJune 7, 202611 min read

Day Eight: The CMS, the Bug That Wasn't, and the First Post Without Code

We installed a proper CMS, migrated 98 posts in a single script, wired hero images to Sanity, and I uploaded the first photo to a guide without touching a single line of code. This is the full story.

Alisha Neilen
Alisha Neilen
Founder, Pawtrips

Day eight. The day Pawtrips stopped being a site that runs on code and started being a site that runs on content.

That is a meaningful distinction. Let me explain what changed and why it matters.

The problem with content living in code

Every tip, every destination guide, every founder post on this site has been living inside TypeScript files. To update a guide, I edit code. To add a post, I write code. To fix a typo anywhere on the site, I need to be at a computer with access to the repository and a working knowledge of how the thing is assembled.

That is fine when it is just me and the site is small. It breaks down the moment I want to involve anyone else, update something from my phone, or manage content at the scale Pawtrips is already at. Ninety-eight published guides is not a small number. Finding a specific entry in a code file that long is its own job.

The solution is a content management system. A CMS. A place where content lives separately from the code, with a proper editing interface, where anyone with access can write and publish without touching a single line of code.

Today I installed one.

Choosing Sanity

The CMS I chose is Sanity. It is headless, which means it stores and delivers content but does not decide how it looks. The Pawtrips frontend you see when you visit the site is still the Next.js application I have been building. Sanity becomes the source of truth for what goes into it.

I chose Sanity because Pawtrips content is structured, not just text. A destination guide is not a blog post. It has beaches with leash rules, cafes with descriptions, a dos list and a donts list, affiliate products placed after specific sections. Sanity handles structured content properly. You define the shape of each document type and the Studio enforces it.

The schema I defined covers two document types: tips and destinations. Each one maps exactly to the fields the templates already expect. Nothing on the frontend needed to change to accommodate the new data source.

The migration

The migration was not a data dump. It was a change in how the live site reads its content.

I wrote four GROQ queries. GROQ is Sanity's query language. It is clean and expressive. The four queries cover everything the site needs: get all tips, get a tip by slug, get all destinations, get a destination by slug. Each one projects only the fields the page actually uses, which keeps the responses small.

Then I updated the pages. The feed listing page now fetches from Sanity on load, using the same useEffect pattern I already had for client-side state. The individual tip and destination templates pull their content from Sanity at build time, with a one hour revalidation window so new content appears without a full redeploy.

Ninety-eight posts. All now reading from the CMS instead of TypeScript files. The static files are still there as a backup, but the live site no longer reads from them.

The old files stay. The new source of truth is Sanity.

The first photo without code

This is the moment I want to document properly.

After the wiring was done, I opened the Sanity Studio in the browser. Clicked into a destination guide. Found the heroImage field. Uploaded a photo.

Published.

Went to the live site. The photo was there.

I did not write a single line of code to make that happen.

That is the whole point of building a proper content infrastructure. The hard work happens once. What you get on the other side is a system where adding a photo to a guide takes thirty seconds and anyone can do it.

For the hero images to work, I needed a URL builder that takes a Sanity image reference and produces a real image URL. The library for this is called sanity/image-url. I set it up as a small helper in the codebase: pass it the image reference from Sanity, chain on a width and format, get back a CDN URL. If a post has no hero image yet, nothing renders. No placeholder, no spinner, nothing. Clean.

Product images

While the schema was open, I added an image field to the affiliate product objects as well. Each product card in a guide can now show a photo above the product title. Same pattern: upload in Studio, appears on the live site.

The old behaviour where products showed no image is still the default. The image is optional. Posts that have no product photos look exactly as they did before.

The bug that was not in the schema

One thing went wrong during the day and I want to be honest about it, because the honest version is more useful than the polished one.

When I first opened the Sanity Studio to test image uploading, the image fields were not clickable. The field was visible in the interface. The upload button was there. But clicking it did nothing.

My first thought was that the schema had an error. Maybe a readOnly property had slipped in somewhere. I checked every field definition. Nothing. The schema was clean.

The actual cause was one level up.

The Pawtrips site has a navigation bar that uses position sticky and a z-index of 50. That navbar is defined in the root layout, which wraps every page on the site, including the Studio. When I embedded the Studio at the /studio route, the sticky navbar was sitting in the DOM above the Studio, capturing clicks meant for the image upload dialogs.

The fix was to make the site chrome conditional. A small client component checks the current URL path and skips rendering the navbar and footer when the path starts with /studio. The Studio now gets a clean full-page environment. Every other route is unchanged.

Diagnosing that took longer than fixing it. The schema was innocent. The Studio config was correct. The bug was in how the application wrapped itself around the Studio.

Verify the actual cause before you change the thing that looks guilty. That lesson keeps coming up.

The building posts are next

The Pawtrips building section, the one you are reading right now, is the final part of the site that still reads from static files. That changes next. The buildingPost document type is already in the Sanity schema. The wiring is the same pattern I used for tips and destinations. It will be live shortly.

When it is, this post will be the first founder story that goes out through the CMS rather than through a code commit.

Eight days in. The infrastructure is real.

Follow along at pawtrips.com.au/building


Alisha Neilen, Founder, Pawtrips

pawtrips.com.au

If this resonated with you, share it with someone who has a dog and a dream.

Join the pack 🐾
More from What We Are Building
Founder Story
Day Ten: The Day I Stopped Building in Isolation
Founder Story
Day Nine, Find a Stay Goes Live