Skip to main content
Use this path when an existing fulldev/ui block in blocks/ already has the layout you want and you need to make it editable through Sanity Studio. The .astro component file stays completely unchanged — you only add the data pipeline. For building an entirely new block with custom rendering, see Adding a custom block.

The adapter pattern

fulldev/ui template blocks define their own Props interface. Sanity stores structured content. The GROQ projection acts as the adapter layer that reshapes Sanity output into the flat props the component expects:
For example, a template block may expect:
Your GROQ projection maps Sanity’s content model to that exact shape:
The template block’s Props interface is the contract you must satisfy. Read the component file first to understand every prop shape before writing the schema.

Files touched per block

Wiring a template block requires changes to exactly 4 files. No .astro file is modified.

Steps

1

Read the component's Props interface

Open the template block file to understand exactly what shape of data it expects. For example, hero-1.astro:
Map each prop to a Sanity field type. Nested objects become object fields. Arrays become array fields. Image src strings typically come from asset->url in GROQ.
2

Create the Sanity schema

Create studio/src/schemaTypes/blocks/hero-1.ts (use the kebab-case name matching the filename). Because this is a template block — not a custom block — you can use defineType directly rather than defineBlock, though defineBlock also works and gives you the base layout fields automatically.
3

Register the schema in index.ts and page schema

In studio/src/schemaTypes/index.ts:
In the page schema (studio/src/schemaTypes/documents/page.ts), add to the blocks[] array:
4

Add the GROQ projection and run typegen

Open astro-app/src/lib/sanity.ts and add a projection fragment for the new block type inside the blocks[] projection. The projection must output fields that match the component’s Props interface exactly.
Run typegen to generate TypeScript types for the new block:
Verify the block renders in local dev:
Add the block to a test page in Sanity Studio (http://localhost:3333) and confirm the fields appear and the rendered output matches the Storybook story for that block.

Common patterns

Images

Template blocks typically expect { src: string; alt: string }. Dereference the asset in GROQ:
For responsive images with width/height, use asset->{ url, metadata { dimensions } } and extract width and height from dimensions.

Nested arrays of objects

When a prop is links?: { text?: string; href?: string }[], the GROQ projection must preserve the array:

Portable Text

If a custom template block’s slot is populated by Portable Text (rare for template blocks), project it as an array and use @portabletext/astro to render it.

Icon names

fulldev/ui components use @iconify/utils icon names (e.g., lucide:arrow-right). Store these as string fields in Sanity — editors type the icon name directly, or you provide a predefined list.

Relationship to the block registry

The template block’s .astro file is already in blocks/ and is already registered in allBlocks with its kebab-case name as the key. The registry requires no changes. After you add the Sanity schema, the block type name (e.g., hero-1) will match the key already in allBlocks['hero-1']. Blocks stored in Sanity with _type: 'hero-1' will dispatch to the correct component automatically.
The name in your Sanity schema must exactly match the .astro filename (without extension). hero-1.ts schema with name: 'hero-1' maps to hero-1.astro. A mismatch causes silent block skipping — BlockRenderer receives undefined from allBlocks[block._type] and renders nothing.

Full reference

For patterns covering all Sanity field types, image handling, nested arrays, Portable Text, and more, see the fulldev/ui to Sanity Conversion Guide in the repository.