Skip to content

Things I've Built

Here is a small sampling of projects I've made.

01 Starter kits 2026

Sanity framework templates

Official starter templates pairing Sanity Studio with frontend meta-frameworks: Next.js, Nuxt, Astro, and SvelteKit.

Links
[Next.js template ]
[Nuxt template ]
[Astro template ]
[SvelteKit template ]

Starter templates matter enormously for a CMS. They serve (at least) three audiences:

  1. Developers who want a starting point. Nobody wants to wire up a Studio, a client, queries and preview from an empty folder for the fifth time. A fresh, up-to-date template is where most real projects begin, and it's the biggest audience by far.
  2. Developers evaluating the platform, whether on their own or because their company is deciding whether to switch or buy. They're the ones feeling out how they like Sanity, testing things, and building something the content editors on their team can try, and a good template makes that call easy.
  3. Agents. Pointing an LLM at a vetted, tried-and-true implementation saves the user tokens and time, and saves the agent from hallucinating its way through.

Each template is a working site (some basic, some with the common bells and whistles) plus a Sanity Studio, both running with one command in about 30 seconds.

npm create sanity@latest -- --template sanity-io/sanity-template-nextjs-clean

I rebuilt the Next.js and Nuxt templates from scratch and maintained the rest of the official starters from late 2024 into 2026. Maintaining meant deciding what a good, simple Sanity project looks like, then keeping every template in step while Sanity and each framework shipped major versions underneath: Next.js v16, Nuxt v4, Astro v6, Svelte v5, Sanity v5. It also meant the unglamorous half of owning anything public: fielding pull requests, working the issue queue, and keeping the READMEs true.

What they have in common is that none of it is a toy. Each one is a real, current Sanity project with the setup already decided, so whatever you build on top of it starts from how the platform is meant to be used.

02 Reference builds 2026

Sanity use case starters

Full reference builds for the hard things people do with a headless CMS: an AI shopping assistant, AI email campaigns, agentic localization.

Links
[AI shopping assistant, live ]
[Use cases on sanity.io ]
[An AI shopping assistant that checks inventory ]
[AI email campaigns from structured content ]
[AI translations that follow your terminology ]
sanity-labs/starters

Use case starters are full, opinionated builds of things that are hard to do with a headless CMS: a shopping assistant that checks stock before it recommends anything, email campaigns generated from content you already have, translation that obeys your glossary. Each installs with the Sanity CLI, and one more command deploys the schema, the functions and the sample data.

They're built to be used three ways:

  • Start a new project from one and ship it.
  • Lift one piece, a schema, a function, a Studio tool, into a codebase you already have.
  • Hand the whole thing to a coding agent as context and say "this, but for my catalog." Each starter ships its own agent skills for exactly that.
pnpm create sanity@latest --template sanity-labs/starters/ai-shopping-assistant

I led the development side of this project when it started in early 2026: the repo, the scaffold new starters are copied from, the CI, etc. I built the AI shopping assistant and the email marketing starter, and co-built the localization use case.

The AI shopping assistant is a Next.js storefront with a Claude chat widget that answers from the catalog through Sanity Context, a read-only MCP server in front of the live dataset. The model gets the schema and runs GROQ against published content at request time, so "a sweater under $100 in a medium" becomes a query with hard filters and comes back as live product cards with current price and stock. The system prompt is a document in Studio, so merchandising can tune it without a deploy. Every conversation is saved to Sanity and classified by Sanity Insights, and a dashboard in Studio shows where shoppers got stuck and what the catalog is missing.

The email marketing starter lets content editors run email campaigns from the CMS they already work in. The brief, the brand voice and the audience segments live in Sanity, so an AI action drafts one promotion per segment from all three, and editors refine and approve it in Studio. Approval fires a Sanity Function that renders the HTML and sends it through Klaviyo's API. Send status and engagement data come back onto the promotion document, so results sit next to the content that produced them.

Agentic localization stores glossaries, style guides, and locale rules as content. Publishing a source document marks its translations stale, a function works out what changed and pre-translates the affected fields through the Agent API, and a reviewer pane in Studio lets a translator apply or redo each suggestion before it publishes. An eval harness scores the output with an LLM judge, which is how you find out whether the glossary is being obeyed.

All three make the same argument underneath: put the rules in structured content and the AI stops guessing.

03 2025

Sanity Functions recipes

Official, ready-to-copy serverless functions for Sanity users. I wrote several of them and helped maintain the library.

Links
[Sanity recipes ]
[Publish once, post everywhere ]
[Automatically tag blog posts ]
[Notify your team in Slack ]
[Capture tone of voice ]
[Brand voice validator ]
[Post to Bluesky ]
[Post to Mastodon ]
Functions examples

Sanity Functions run your own code on Sanity's infrastructure whenever content changes. I helped build the recipe library that goes with them: official, ready-made functions anyone can add to a project with one command and have running before lunch.

npx sanity blueprints add function --example slack-notify

Publish a post and a Slack channel hears about it. Publish once and a function posts it to Bluesky, Mastodon, LinkedIn and five more platforms, formatted for each. Another one tags posts for you, reusing the tags already in the dataset before the model is allowed to invent a new one, which is exactly the kind of chore an AI should be doing in the background instead of an editor. Another checks a draft against the brand voice and writes the suggestions back into the document. I wrote a good share of the library's 21 recipes, including the ones linked below, and kept them working as the product shipped underneath them.

A function is a serverless handler declared in a blueprint next to the Studio and triggered by a content event. The GROQ filter on that trigger is the whole ballgame: without a tight one, every publish in the dataset invokes the function, which is a correctness bug and a bill at the same time. The projection matters for the same reason, so the handler only receives the fields it uses. The AI recipes go through Agent Actions instead of raw model calls, so the model writes into the document through the schema rather than handing back a blob of text to parse. Every recipe is a self-contained folder: handler, blueprint config, a sample event so it runs locally against a real document shape, and a README with the env vars. That structure is what let them keep pace with a handler signature and CLI that kept changing after the library was public.

The point of all of it is time to a working function. A developer who has one running in ten minutes will try the next one, wire it into a real project, and fork it into something the recipe never anticipated. A developer who starts from an empty handler usually closes the tab. The recipes exist so the first thing happens.

04 Agency 2023

AllDay

The web agency I co-founded and ran for seven years. I built the site, the brand, and everything on its work page.

Links
[allday.io ]
[The work ]

In 2015 a friend and I were both freelancing into the same ceiling, so we started an agency. My business partner ran sales and projects, I ran design and code. Most of what we shipped, including allday.io itself, ran on Gatsby, Netlify and Sanity, with SvelteKit showing up in the later builds, and that's where I got good at Sanity. Not from a tutorial, from shipping it for clients who had to edit it themselves on a Tuesday. A few years later Sanity hired me.

The work covered a lot of ground. A Dunkin' franchise that needed a locations directory and a hiring funnel. A delivery app that needed to launch on time and fill orders without dropping any. A startup MVP with a custom admin panel and transactional email, on a startup budget. Three sites for Red Ventures, who knew my work from my time there and came back for more after I'd left. An architecture firm, a branding firm, a home builder.

The designs and the code are four to five years old now, which is why they aren't listed here one by one. Some of it predates tools I'd reach for today. It's still work I'm proud of, and it shows what two people can ship when nothing gets handed off. The case studies are all on allday.io. We closed AllDay in 2023 while it was doing better than it ever had, because we both wanted to explore new career opportunities.

05 Side project 2021

Birdables

Collectible bird trading cards I designed during COVID. Every card designed in Figma and printed on 100% recycled paper.

Links
[birdables.com ]
[How the cards are made ]
  • Roseate Spoonbill. Every card carries a rarity rating, a call waveform and a range map.
  • Every element on a card, and what it is for.
  • One layout, twenty-one birds, no two reading the same.

During COVID I got into birding, and then further into it than anyone around me was prepared to deal with. Birdables came out of that.

It's a set of collectible bird trading cards, standard 2.5 by 3.5 inches. Nobody hired me to make them. It was a passion project start to finish: I designed every card in Figma, built the brand around them, directed the product photography and built the site they live on.

Every card is accurate. The common and scientific names, a waveform of the bird's call or song, a habitat map of where the species actually lives, its conservation status, and a rarity from one to five stars worked out from that status and how hard the bird is to spot. Each card carries a working QR code that lands on that bird's page on the site. The set stands at 21 species. Every bird gets a card eventually. I'm not in a hurry.

Bird habitat disappears every year, and I didn't want a bird product to be part of that. The cards are printed on 100% recycled paper, and I turned down foil backing, which is standard in trading cards and looks great, because foil-backed stock can't be recycled. A share of what the cards earned went to the Cornell Lab of Ornithology and Audubon.

The same thinking went into the site. It's SvelteKit on Netlify, and every page is prerendered: each route exports prerender = true, and the dynamic bird route exports an entries function listing every species, so the build pulls the content from Sanity once and writes plain HTML for all of it. Visitors get static files from a CDN instead of a server call per view, and the card art is served as WebP at the sizes it's shown. The repo is public.