A Designer-Operated Portfolio With No Developer in the Loop
Designer-Operated PortfolioMarketing / Portfolio Site
A headless Next.js marketing and portfolio site for an interior design studio, backed by a Sanity-managed schema covering projects, artwork, journal posts, and curated product picks. Built with App Router, ISR, and a self-serve Studio so the designer can publish without a developer. The studio needed a portfolio site they could update themselves: new projects, new artwork, new product picks, all without filing a ticket. Off-the-shelf builders couldn't model the relationships between a project and the products specified inside it, and a custom CMS would have left the studio dependent on us forever. The brief was a designer-operated site that still rendered like a hand-built one.
Industry
Interior design and fine art
Independent interior design studio
Independent design practices need editorial-quality portfolios that double as a sourcing record, not the templated grid-of-thumbnails that every Squarespace site collapses into.
Existing studio rebuilding their web presence to pair finished-project storytelling with an ongoing journal and a curated picks section that drives the studio's point of view.
A Portfolio That Edits Itself
Four content types, one editorial voice
Portfolio projects, artwork, journal posts, and picks each have their own shape, but every page links across them. Modeling that without forcing the studio to duplicate content was the central schema problem.
Mixed media in the hero slot
Some projects open on a still photograph, others on a silent looping video. The schema had to enforce that exactly one of the two was provided, plus a static grid thumbnail when video was used so listings stay quiet.
Project and product crosslinks
A finished interior should surface the actual chairs, lights, and finishes used. That meant a referenced relationship between portfolio and picks, queryable in both directions, without making the editor rebuild the list each time.
Designer-friendly Studio
The client publishes their own content. Validation, conditional fields, and grid-layout previews had to do the work that a developer review otherwise would.
No stale pages, no rebuild storms
New picks and journal posts go up frequently. The site needed to refresh quickly without redeploying on every edit.
User-First, AI-Native Development
Discovery & Research
- •Audited the studio's existing site to map which content types actually drove traffic and which were vestigial
- •Walked through the editor's publishing workflow to identify where a CMS would help versus where it would just add ceremony
- •Reviewed reference portfolios in the interior-design space to calibrate density, motion, and gallery behavior
Architecture Design
- •Next.js 16 App Router with React 19 server components for every public route, client components reserved for genuinely interactive surfaces (lightbox, search modal, header dropdown)
- •Sanity CMS as the single source of truth, queried via GROQ through next-sanity
- •Studio mounted at /studio inside the same Next.js app so the editor never leaves the site to publish
- •Resend for the contact form, kept as a thin server action with field validation
- •ISR with a 60-second revalidate window so edits go live without a redeploy
Core Technical Decisions
1. Sanity over a database-backed CMS
The content shape is editorial, not transactional. Sanity's schema-as-code meant the project, picks, and journal models live in version control next to the components that render them, and Studio is generated from those schemas. We rejected a generic headless DB because there was no need for write paths beyond the contact form.
2. Server components by default
Every listing and detail page fetches GROQ in a server component and passes typed data to a client wrapper only where motion or modal state is needed. This keeps the JS bundle small and image URLs signed server-side via @sanity/image-url.
3. Conditional schema fields with custom validation
The portfolio schema branches on heroMediaType. When a project is video, the featured image hides and a gridThumbnailImage becomes required so listing pages never render a video tag in a grid cell. The validation runs in Studio, not at render time, so the editor catches mistakes before publish.
4. Referenced picks instead of embedded ones
portfolio.relatedPicks is an array of references that dereferences in the detail query. One pick can appear on many projects without being duplicated, and updating a product link updates it everywhere.
5. Orderable documents for editorial control
The studio wanted to hand-order projects, not sort by date. Adding orderable ranks via @sanity/orderable-document-list to portfolio, artwork, journal, and picks gave them drag-and-drop ordering in Studio that the GROQ queries respect via order(orderRank).
6. ISR over on-demand revalidation
A 60-second revalidate window was the right tradeoff for a site that publishes a few times a week. Webhook-driven revalidation would have been more code for a benefit the editor would never feel.
Iterative Development Phases
- 1.Stand up the four schemas and query them through GROQ before any styling
- 2.Wire Studio at /studio and validate the editor flow end to end
- 3.Build portfolio listing and detail with image hero only
- 4.Layer in video hero support, grid thumbnails, and lightbox
- 5.Add picks, journal, artwork, and the cross-references between them
- 6.Contact form, navigation visibility controls, homepage composition
Key Features Delivered
Portfolio system with mixed media heroes
- •Image or video hero per project, enforced at the schema level
- •Required grid thumbnail when hero is video, so listings stay static
- •Hotspot-aware images for sane cropping at every breakpoint
Product picks linked to projects
- •Dedicated picks content type with image, excerpt, external link, and seller
- •relatedPicks references on portfolio projects, dereferenced in the detail query
- •ProjectPicksSection and PicksByProjectGallery surface picks both ways
Editor-controlled navigation and homepage
- •Per-menu-item visibility toggles in navigationSettings so sections can be hidden without code changes
- •Homepage settings document drives featured portfolio, artwork, journal, and picks blocks
Image lightbox with thumbnail strip
- •Full-screen gallery with keyboard navigation and a thumbnail rail for orientation in long galleries
Journal with categories and rich text
- •Portable Text rendering through @portabletext/react for inline images, links, and structured content
Contact form with server-side delivery
- •Resend integration behind a validated API route; sender address and recipient pulled from env, no secrets in client code
Studio at /studio with @sanity/vision
- •Editor and developer share one URL: the client publishes; we debug GROQ in the same surface
Technical Highlights
Frontend
Next.js 16 (App Router), React 19, TypeScript 5, Tailwind CSS 4
Backend
Next.js server components and route handlers, ISR with 60s revalidate
APIs & Integration
Sanity CMS 4 (next-sanity, @sanity/image-url, @sanity/vision, @sanity/orderable-document-list), Resend for transactional email, Portable Text via @portabletext/react
DevOps
Vercel-class hosting, GitHub-managed schema-as-code
Production-Ready Success
Shipped a production site spanning portfolio, artwork, journal, picks, about, and contact, all driven by Sanity
Editor publishes new projects, posts, and picks without a developer in the loop, with Studio validation catching missing alt text, missing video thumbnails, and broken hero configurations before publish
Cross-linked picks and projects in a single GROQ round trip, so a project page renders its sourced products with no follow-up fetches
Navigation and homepage composition fully editor-controlled, including per-item visibility, so sections can be hidden during seasonal pushes without a deploy
Server-rendered detail pages with hotspot-aware Sanity image URLs, keeping the public bundle to interactive surfaces only
ISR with a 60-second window means edits propagate quickly without rebuild churn
Need a Site Your Editors Can Actually Run?
If you're shipping editorial work and tired of filing tickets to update your own pages, a headless build with a tightly modeled Studio gets you out of that loop. We design schemas that prevent broken publishes, queries that render in one round trip, and Studios that feel like the editor's tool, not the developer's.