Instruction file imported from sonnysangha/ecommerce-ai-nextjs-16-sanity-clerk-agentkit-stripe-checkout-vercel-ai-agents (
.cursor/rules/Agents.mdc). Copyright stays with the author.
Your role
You are a principal-level TypeScript and React engineer who writes best-practice, high performance code. You are also an expert on structured content modelling.
UI/UX
Always use Shadcn components and Tailwind CSS where possible, use the Shadcn MCP server to identify which components are available and how to install them.
- NEVER try to create a Shadcn component yourself, it must always come through the command line
- NEVER modify the actual component once imported, use classNames to modify an imported component instead.
Sanity Studio Schema Types
Content modelling
Unless explicitly modelling web pages or app views, model content that describes what things are, not what they look like:
- Good examples describe what things are:
status,tone,visibility,role - Bad examples describe what things look like:
color,font-size,border-radius
Basic schema types
- ALWAYS use the
defineType,defineField, anddefineArrayMemberhelper functions - ALWAYS write schema types to their own files and export a named
constthat matches the filename - ONLY use a
nameattribute in fields unless thetitleneeds to be something other than a title-case version of thename - ANY
stringfield type with anoptions.listarray with fewer than 5 options must useoptions.layout: "radio" - ANY
imagefield must includeoptions.hotspot: true - INCLUDE brief, useful
descriptionvalues if the intention of a field is not obvious - INCLUDE
rule.warning()for fields that would benefit from being a certain length - INCLUDE brief, useful validation errors in
rule.required().error('<Message>')that signal why the field must be correct before publishing is allowed - AVOID
booleanfields, write astringfield with anoptions.listconfiguration - ONLY use a single reference when there is no possibility that more than one value will be required: examples include
city,country - ALWAYS use an array of references when there is any possibility more than one value will be required: examples include
authors,categories - CONSIDER the order of fields, from most important and relevant first, to least often used last
// ./src/schemaTypes/lessonType.ts
import { defineField, defineType } from "sanity";
export const lessonType = defineType({
name: "lesson",
title: "Lesson",
type: "document",
fields: [
defineField({
name: "title",
type: "string",
}),
defineField({
name: "categories",
type: "array",
of: [defineArrayMember({ type: "reference", to: { type: "category" } })],
}),
],
});
Schema type with custom input components
- If a schema type has input components, they should be colocated with the schema type file. The schema type should have the same named export but stored in a
[typeName]/index.tsfile:
// ./src/schemaTypes/seoType/index.ts
import { defineField, defineType } from "sanity";
import seoInput from "./seoInput";
export const seoType = defineType({
name: "seo",
title: "SEO",
type: "object",
components: { input: seoInput },
// ...
});
No anonymous reusable schema types
Any field type that can be reused in multiple document types should be registered as its own custom schema type.
// ./src/schemaTypes/blockContentType.ts
import { defineField, defineType } from "sanity";
export const blockContentType = defineType({
name: "blockContent",
title: "Block content",
type: "array",
of: [defineField({ name: "block", type: "block" })],
});
Decorating schema types
Every document and object schema type should:
- Have an
iconproperty from@sanity/icons - Have a customized
previewproperty that shows rich contextual details about the document - Use
groupswhen the schema type has more than a few fields to collate related fields and only show the most important group by default. Thesegroupsshould use the icon property as well. - Use
fieldsetswithoptions: {columns: 2}if related fields could be grouped visually together, such asstartDateandendDate
Validation rules for fields
- ALWAYS make fields
requiredif a document should not be published without that field meeting a criteria - ALWAYS give a validation
warningif a field value should meet a certain criteria - ALWAYS contain a custom
errormessage to signal why the field must be correct, or how it could be improved to satisfy the rule - ALWAYS put validation rules in an array, and order them from most important to least important
- Use
.custom()to enforce validation rules that cannot be expressed with other validation methods, such as checking the value of another field from the document
// ./src/schemaTypes/slugType/index.ts
import { defineField, defineType } from "sanity";
export const slugType = defineType({
name: "slug",
title: "Slug",
type: "object",
validation: (Rule) => [
Rule.custom((value, context) =>
value?.current && value?.current.length > 100
? "Slug cannot be longer than 100 characters"
: true
),
Rule.required().error("Required to generate a URL"),
],
// ...
});
Testing Studio configuration
After making changes to schema or studio configuration, test the configuration with the following scripts. Always run all three scripts after making changes.
Add these scripts to package.json to test the Studio configuration:
// package.json
{
// existing configuration...
"scripts": {
// existing scripts...
"typegen": "sanity schema extract && sanity typegen generate --enforce-required-fields",
"typecheck": "tsc --noEmit"
}
}
- Ensure TypeScript can compile with
npm run typecheck - Ensure schema types are valid for export with
npm run typegen - Ensure the Studio can be built with
npm run build
Writing Sanity content
Write using Sanity MCP Server
- ALWAYS use the Sanity MCP Server if available use to query and create content
Write and import using Sanity CLI
If you do not have the Sanity MCP server installed:
- ONLY use the existing schema types registered in the Studio configuration
- ALWAYS write content as an
.ndjsonfile at the root of the project, where each line is a single JSON object representing a document - NEVER write scripts to write content, just write the
.ndjsonfile - IMPORT
.ndjsonfiles using the CLI commandnpx sanity dataset import <filename.ndjson> - NEVER include a
.in the_idfield of a document unless you need the document to be private - NEVER include image references if you do not know which image documents exist
- ALWAYS if the full URL of an image or file is known, use it in the
_sanityAssetfield, for example:
{"_type":"image","_sanityAsset":"image@https://{url-to-image}"}
{"_type":"file","_sanityAsset":"file@https://{url-to-file}"}
Writing GROQ queries
- ALWAYS use
SCREAMING_SNAKE_CASEfor variable names, for examplePOSTS_QUERY - ALWAYS import the
defineQueryfunction to wrap query strings from thegroqornext-sanitypackage - ALWAYS write every required attribute in a projection when writing a query
-- DO NOT use the
...operator to project all attributes - ALWAYS put each segment in a filter, and each attribute in a projection its own line
- ALWAYS use parameters for variables in a query -- DO NOT insert dynamic values using string interpolation
// ✅ Good GROQ query example
import { defineQuery } from "groq";
export const POST_QUERY = defineQuery(`*[
_type == "post"
&& slug.current == $slug
][0]{
_id,
title,
image,
author->{
_id,
name
}
}`);
TypeScript generation
For monorepos with a studio and a front-end
- ALWAYS use a simple pnpm workspace configuration to place the studio in
apps/studio
your-project/
└── apps/
├── studio/ -> Sanity Studio
└── web/ -> Front-end
- ALWAYS extract the schema to the web folder with
npx sanity@latest schema extract --path=../<front-end-folder>/sanity/extract.json - ALWAYS generate types with
npx sanity@latest typegen generateafter every GROQ query change - ALWAYS create a TypeGen configuration file:
// apps/studio/sanity-typegen.json
{
"path": "./**/*.{ts,tsx,js,jsx}",
"schema": "./<front-end-folder>/sanity/extract.json",
"generates": "./<web-folder>/sanity/types.ts"
}
For the front-end
- ONLY write Types for document types and query responses if you cannot generate them with Sanity TypeGen
Looking for help
Sanity CLI provides many ways to interact with Sanity projects, datasets and search documentation and API's.
- To understand Sanity product features search the documentation with
npx sanity docs search "<query>" - To see available OpenAPI endpoints for a project, run
npx sanity openapi list - To see available CLI commands, run
npx sanity --help