Every block — custom or template — follows a single consistent architecture. Understanding these four mechanics makes it straightforward to add, debug, or extend any block.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/gsinghjay/astro-shadcn-sanity/llms.txt
Use this file to discover all available pages before exploring further.
The flat-props interface pattern
All block components declare their props by extending the generated TypeScript type for that block:Astro.props — not from a nested block object. This is what “flat props” means: heading, not block.heading.
The types in
@/lib/types are generated automatically by npm run typegen from the Sanity schema. Run typegen after any schema change to keep props in sync.Filename → _type mapping
The registry auto-discovers blocks by scanning two directories. The mapping rule depends on which directory the file lives in.
Custom blocks: PascalCase → camelCase
Files inblocks/custom/ use PascalCase filenames. The registry lowercases the first character to produce the _type:
| Filename | _type |
|---|---|
HeroBanner.astro | heroBanner |
CtaBanner.astro | ctaBanner |
FaqSection.astro | faqSection |
SponsorCards.astro | sponsorCards |
LogoCloud.astro | logoCloud |
_type must match the name field in the corresponding Sanity schema definition exactly.
Template blocks: kebab-case used directly
Files inblocks/ use kebab-case filenames. The registry uses the filename as-is:
| Filename | _type |
|---|---|
hero-1.astro | hero-1 |
features-3.astro | features-3 |
cta-2.astro | cta-2 |
Auto-discovery via import.meta.glob
The block registry uses Vite’s import.meta.glob to scan both directories at build time. You do not register blocks manually anywhere:
BlockRenderer: the dispatch layer
BlockRenderer.astro receives the page’s blocks[] array and renders each one:
allBlocks[block._type]resolves the component. If no component matches, the block is silently skipped.getBlockExtraPropsinjects resolved document references (sponsors, projects, testimonials, events) for blocks that need them.- Props are spread directly:
<Component {...block} {...extraProps} />. The component receives all Sanity fields as top-level props.
BlockWrapper: the layout shell
BlockWrapper.astro wraps every block with layout context derived from three base fields present on every block schema:
stegaClean call strips Visual Editing stega metadata from string values so CSS class lookups work correctly in the preview branch.
The data-gtm-section attribute enables Google Tag Manager to attribute user interactions to the correct block type without additional JS.
Base fields injected by defineBlock
These three fields are added automatically to every block schema by the defineBlock helper — block authors never declare them manually:
| Field | Values | Default |
|---|---|---|
backgroundVariant | white, light, dark, primary | white |
spacing | none, small, default, large | default |
maxWidth | narrow, default, full | default |
Flat array only
Blocks are a flat array on the page document. There are no nested blocks. A block can contain objects (e.g., an array ofstatItem objects inside StatsRow), but those objects are not themselves blocks registered in the registry.