Use Your Own Product

(The Alternative Is 47 Clicks vs One CTRL-V)

TL;DR

Sanity's default input for custom table schemas renders as a nested array of objects: click "Add row," click into cell 1, type, click into cell 2, type, repeat until retirement. The official table plugin has a grid UI but no clipboard paste. Claude Code built a custom spreadsheet-style input with real Ctrl+V support, then wrote a migration script that ate 26 legacy HTML tables in 14 seconds. This is that story.

The Setup: A Perfectly Good Schema

When told Claude to add tables to Sugartown's content model, the design was clean. A tableBlock custom block type inside Portable Text, a variant selector, a header-row toggle, and an array of rows, each containing an array of string cells. The frontend Table component rendered it beautifully with design system tokens.

I was pleased with myself. I moved on.

And then I tried to actually author a table.

The Horror: Arrays All the Way Down

Sanity's default rendering for an array-of-objects schema is, predictably, an array-of-objects input. Click "Add item" to create a row. The row opens a modal. Inside the modal: another array field for cells. Click "Add item" again. Type your cell value. Close the modal. Repeat.

A 4-column, 6-row comparison table (the kind you'd knock out in Google Sheets in 90 seconds) required approximately 47 clicks in Studio. I counted. I wish I hadn't.

Now add this: 26 documents from the WordPress migration sitting in opaque htmlSection blocks with raw <table> HTML inside them. Not editable. Not styled. Just sitting there wearing their WordPress markup like a vintage concert tee they refuse to throw away (I have a bin in the basement, remind me).

Manually re-entering 26 tables through that interface was not going to happen.

The Hope: Surely Someone Has Solved This

Claude suggested @sanity/table. Millions of weekly npm downloads. It renders a real grid of TextInput components, actual cells you can see and click into. A genuine step up from modal inception.

Then I tried to paste.

I copied a 5-column table from Google Sheets, clicked the first cell, pressed Ctrl+V, and the entire tab-delimited payload landed in one cell as a single string. With literal tab characters.

Delightful.

Each cell is its own separate text box, and there's no way to paste data into it from a spreadsheet. The plugin makes tables look nicer in the editing interface, but it doesn't make them easier to actually use. On top of that, it's missing features we'd already built into our own version (like choosing a table style or marking the first row as a header). If I switched to it, we'd have to build workarounds for the missing features, restructure how my table data is stored, and update every document that already has a table in it. All for a tool that still can't handle paste.

Hard pass.

The Fix: Build the Spreadsheet You Wish You Had

Sanity lets you completely replace the default editing interface for any content type with your own custom component. It's one of the platform's genuine strengths.

So I asked for one.

The custom table editor renders as an actual spreadsheet grid inside Sanity Studio. You click a cell, type in it, hit Tab to move to the next one, Enter to go down, arrow keys to navigate. You can paste directly from Excel, Google Sheets, or a browser table, and the grid resizes to fit. You can add and remove rows and columns without leaving the grid.

The important part: nothing changed under the hood. The data structure stayed exactly the same. Every query, every API response, every table on the live site kept working. The only thing that changed was the editing experience, from a wall of nested text boxes to something that behaves like a spreadsheet.

The Migration: 26 Tables, 14 Seconds

With the editing experience sorted, the next job was cleaning up 26 tables that had been carried over from WordPress: raw HTML sitting in content blocks, invisible to editors in Studio, and completely untouched by the design system.

I pointed Claude Code at the problem: "there are 26 tables stuck in HTML blocks, migrate them." Four minutes later I had a script. It parses the HTML, extracts each table into the structured format, converts the surrounding text into proper content blocks, and swaps out the opaque HTML block for a clean, editable one. It detects header rows, cleans up encoding artefacts, strips empty spacing rows, and normalises column counts.

Preview mode first (always). 27 documents scanned, 26 flagged, 24 tables extracted. Then the real run. All 26 patched, zero errors. Ran the preview again: nothing left to patch. Clean.

Every one of those tables is now editable, styled, and pasteable.

The Lesson: Schema Design Is Not UX Design

The table schema was correct from day one. The data structure was right. The queries were right. The frontend rendering was right. But the editing experience was terrible, and I didn't notice because I'd been testing with sample data injected through the API, not by actually sitting in the editor and trying to build a table the way an editor would.

This is the headless CMS blind spot nobody talks about. You can model content perfectly and still deliver an editing experience that makes your authors open a Google Doc instead. The schema is the API. The input component is the product. If the product takes 47 clicks to create a table, the API is irrelevant.

Sanity's custom input API is the escape hatch, and arguably the feature that separates it from every other headless CMS on the market. But you have to actually use it. And that means sitting in your own editor and trying to do the thing you're asking editors to do.

Clipboard paste is table stakes. Pun intended.