sanity — скилл для Claude Code: схемы, GROQ и TypeScript
Скилл для работы с Sanity CMS: помогает описывать схемы документов, писать GROQ-запросы и связывать контент с TypeScript.
npx skills add Mindrally/skills --skill sanity -a claude-codeЧто делает sanity
Скилл задаёт агенту роль специалиста по Sanity CMS и headless-архитектуре. Внутри — готовые шаблоны: defineType и defineField для документов и объектов, массив blockContent с блоками, картинками и ссылками, конфигурация preview для списка документов в студии.
Отдельный блок про GROQ: выборки всех постов с сортировкой по publishedAt, запрос по slug.current, пагинация через [$start...$end], select для условных полей и coalesce для запасных значений. Рядом — настройка клиента @sanity/client с projectId, dataset и apiVersion, плюс @sanity/image-url для ссылок на изображения. Токен доступа и переменные окружения придётся завести самому: без projectId и dataset клиент не соберётся.
Есть разделы про генерацию типов из схемы, кастомную валидацию (например, проверку домена в email) и собственные input-компоненты. Скилл советует использовать ссылки между документами, portable text для rich-контента и настроить CORS с правами API. Полезен, если проект уже на Sanity — на пустом месте он вам ничего не создаст.
Какие задачи решает
- описать схему документа с полями, slug и валидацией
- написать GROQ-запрос с сортировкой и пагинацией
- связать документы через reference и вытащить поля по стрелке
- настроить клиент Sanity с projectId, dataset и apiVersion
- добавить кастомную проверку поля в студии
Примеры запросов агенту
- Помоги описать схему документа post с полями title, slug, author и publishedAt
- Напиши GROQ-запрос для всех постов с сортировкой по publishedAt и пагинацией
- Настрой клиент Sanity и urlFor для картинок в lib/client.ts
Как установить sanity
1Через Skills CLI (Vercel)
npx skills add Mindrally/skills --skill sanity -a claude-code2Вручную: положить файлы
~/.claude/skills/sanity/SKILL.mdПапка скилла целиком; для одного проекта — .claude/skills/sanity/
1Через Skills CLI (Vercel)
npx skills add Mindrally/skills --skill sanity -a codex2Вручную: положить файлы
.agents/skills/sanity/SKILL.mdДля всех проектов — ~/.agents/skills/
1Через Skills CLI (Vercel)
npx skills add Mindrally/skills --skill sanity -a cursor2Вручную: положить файлы
.cursor/skills/sanity/SKILL.mdCursor читает и .agents/skills/, и .claude/skills/
1GitHub CLI
gh skill install Mindrally/skills sanity2Вручную: положить файлы
.github/skills/sanity/SKILL.md1Через Skills CLI (Vercel)
npx skills add Mindrally/skills --skill sanity -a gemini-cli2Командой платформы
gemini skills install https://github.com/Mindrally/skills --path sanityВручную: положить файлы
.devin/skills/sanity/SKILL.mdСтарый путь .windsurf/skills/ тоже читается
Через Skills CLI (Vercel)
npx skills add Mindrally/skills --skill sanity -a opencodeClaude.ai
Упакуйте папку в zip и загрузите в Settings → Capabilities → Skills
По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов
Вопросы о sanity
Как установить sanity в Claude?
Выполните npx skills add Mindrally/skills --skill sanity -a claude-code. Файл скилла появится по пути ~/.claude/skills/sanity/SKILL.md.
Что делает скилл sanity?
Даёт агенту правила разработки на Sanity CMS: схемы документов и объектов, GROQ-запросы, интеграция с TypeScript, валидация и организация проекта.
Для каких платформ подходит sanity?
Скилл ставится в Claude, Codex, Cursor, GitHub Copilot, Gemini CLI и OpenCode — для каждой платформы своя команда установки.
Исходный текст
SKILL.md · Apache-2.0
---
name: sanity
description: Sanity CMS development guidelines for schema creation, GROQ queries, TypeScript integration, and project organization
metadata:
maintainer: Mindrally
source: https://github.com/Mindrally/skills
---
# Sanity CMS Development
You are an expert in Sanity CMS, GROQ queries, TypeScript integration, and headless CMS architecture.
## Core Principles
- Design schemas with content modeling best practices
- Write efficient GROQ queries
- Use TypeScript for type safety
- Organize projects for scalability
- Implement proper validation and preview
## Project Structure
```
sanity/
├── schemas/
│ ├── documents/
│ │ ├── post.ts
│ │ └── author.ts
│ ├── objects/
│ │ ├── blockContent.ts
│ │ └── image.ts
│ └── index.ts
├── lib/
│ ├── client.ts
│ └── queries.ts
├── components/
│ └── previews/
└── sanity.config.ts
```
## Schema Definition
### Document Types
```typescript
// schemas/documents/post.ts
import { defineType, defineField } from 'sanity';
export default defineType({
name: 'post',
title: 'Post',
type: 'document',
fields: [
defineField({
name: 'title',
title: 'Title',
type: 'string',
validation: (Rule) => Rule.required().min(10).max(80),
}),
defineField({
name: 'slug',
title: 'Slug',
type: 'slug',
options: {
source: 'title',
maxLength: 96,
},
validation: (Rule) => Rule.required(),
}),
defineField({
name: 'author',
title: 'Author',
type: 'reference',
to: [{ type: 'author' }],
}),
defineField({
name: 'publishedAt',
title: 'Published at',
type: 'datetime',
}),
defineField({
name: 'body',
title: 'Body',
type: 'blockContent',
}),
],
preview: {
select: {
title: 'title',
author: 'author.name',
media: 'mainImage',
},
prepare({ title, author, media }) {
return {
title,
subtitle: author ? `by ${author}` : '',
media,
};
},
},
});
```
### Object Types
```typescript
// schemas/objects/blockContent.ts
import { defineType, defineArrayMember } from 'sanity';
export default defineType({
name: 'blockContent',
title: 'Block Content',
type: 'array',
of: [
defineArrayMember({
type: 'block',
styles: [
{ title: 'Normal', value: 'normal' },
{ title: 'H2', value: 'h2' },
{ title: 'H3', value: 'h3' },
{ title: 'Quote', value: 'blockquote' },
],
marks: {
decorators: [
{ title: 'Strong', value: 'strong' },
{ title: 'Emphasis', value: 'em' },
{ title: 'Code', value: 'code' },
],
annotations: [
{
name: 'link',
type: 'object',
title: 'URL',
fields: [
{
name: 'href',
type: 'url',
title: 'URL',
},
],
},
],
},
}),
defineArrayMember({
type: 'image',
options: { hotspot: true },
}),
],
});
```
## GROQ Queries
### Basic Queries
```typescript
// lib/queries.ts
// Get all posts
export const allPostsQuery = groq`
*[_type == "post"] | order(publishedAt desc) {
_id,
title,
slug,
publishedAt,
"author": author->name,
"imageUrl": mainImage.asset->url
}
`;
// Get single post by slug
export const postBySlugQuery = groq`
*[_type == "post" && slug.current == $slug][0] {
_id,
title,
body,
publishedAt,
"author": author->{name, image},
"categories": categories[]->title
}
`;
// Pagination
export const paginatedPostsQuery = groq`
*[_type == "post"] | order(publishedAt desc) [$start...$end] {
_id,
title,
slug,
excerpt
}
`;
```
### Advanced GROQ
```typescript
// Conditional projections
export const conditionalQuery = groq`
*[_type == "post"] {
title,
"content": select(
defined(body) => body,
"No content available"
)
}
`;
// Coalesce for fallbacks
export const fallbackQuery = groq`
*[_type == "post"] {
"displayTitle": coalesce(seoTitle, title)
}
`;
// References
export const withReferencesQuery = groq`
*[_type == "post" && references($authorId)] {
title,
publishedAt
}
`;
```
## Client Setup
```typescript
// lib/client.ts
import { createClient } from '@sanity/client';
import imageUrlBuilder from '@sanity/image-url';
export const client = createClient({
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!,
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET!,
apiVersion: '2024-01-01',
useCdn: process.env.NODE_ENV === 'production',
});
const builder = imageUrlBuilder(client);
export function urlFor(source: any) {
return builder.image(source);
}
```
## TypeScript Integration
```typescript
// Generate types from schema
import { Post, Author } from '@/sanity/types';
export async function getPosts(): Promise<Post[]> {
return client.fetch(allPostsQuery);
}
export async function getPost(slug: string): Promise<Post | null> {
return client.fetch(postBySlugQuery, { slug });
}
```
## Validation
```typescript
defineField({
name: 'email',
type: 'string',
validation: (Rule) =>
Rule.required()
.email()
.custom((email) => {
if (email && !email.endsWith('@company.com')) {
return 'Must be a company email';
}
return true;
}),
});
```
## Custom Components
```typescript
// Custom input component
import { StringInputProps } from 'sanity';
export function CustomStringInput(props: StringInputProps) {
return (
<div>
<label>{props.schemaType.title}</label>
{props.renderDefault(props)}
<span>{props.value?.length ?? 0} characters</span>
</div>
);
}
```
## Best Practices
- Use references for relationships between documents
- Implement proper validation rules
- Create meaningful preview configurations
- Use portable text for rich content
- Optimize images with Sanity's image pipeline
- Set up proper CORS and API permissions
© Mindrally · Apache-2.0 · Mindrally/skills