Every Propify component includes production-ready Sanity schemas. This guide explains how Sanity CMS works with Propify and how to integrate the schemas into your content workflow.
Sanity is a flexible, API-first content platform that treats content as structured data. Unlike traditional CMSs, Sanity allows you to:
The combination provides:
A Sanity schema defines the structure of your content:
export const heroSchema = defineType({ name: 'hero', title: 'Hero Section', type: 'document', fields: [ defineField({ name: 'headline', title: 'Headline', type: 'string', validation: Rule => Rule.required() }), defineField({ name: 'subheadline', title: 'Subheadline', type: 'text', rows: 3 }) ]})
Each Propify component schema:
Example schema structure:
// Propify naming conventionexport const pricing05Module = defineType({ name: 'module.pricing05', // Namespaced name title: 'Pricing Cards', // Display name type: 'object', // Module type icon: IconComponent, // Visual identifier groups: [ // Organize fields { name: 'content', title: 'Content' }, { name: 'settings', title: 'Settings' } ], fields: [...] // Component props})
# Create new Sanity projectnpm create sanity@latest# Or add to existing projectnpm install sanity
// schemas/modules/pricing05.tsimport { pricing05Module } from './propify/pricing05.schema'// schemas/index.tsexport const schemaTypes = [ pricing05Module, // ... other schemas]
defineType({ name: 'page', type: 'document', fields: [ defineField({ name: 'modules', type: 'array', of: [ { type: 'module.pricing05' }, // ... other modules ] }) ]})
Use GROQ to fetch your component data:
// Basic queryconst query = `*[_type == "page" && slug.current == $slug][0]{ modules[]{ _type, _key, ... }}`;// Detailed query with expansionconst query = `*[_type == "page" && slug.current == $slug][0]{ modules[]{ _type == "module.pricing05" => { title, yearlyDiscount, plans[]{ name, price, features[] } } }}`;
Generate TypeScript types from your schemas:
// Use in your codeimport { Pricing05Module } from '@/sanity.types'function renderModule(module: Pricing05Module) { return <Pricing05 {...module} />}
// Good - flat structurefields: [ { name: 'title', type: 'string' }, { name: 'price', type: 'number' }]// Avoid deep nesting when possible
// Reference shared contentdefineField({ name: 'author', type: 'reference', to: [{ type: 'person' }]})
defineField({ name: 'price', type: 'number', validation: Rule => Rule .required() .min(0) .error('Price must be positive')})
Enhance the editing experience:
// Custom input componentdefineField({ name: 'color', type: 'string', components: { input: ColorPickerInput }})// Conditional fieldsdefineField({ name: 'ctaUrl', type: 'url', hidden: ({ parent }) => !parent?.showCta})