Sanity Best Practices
SanityOfficialPublished by Sanity
- Skill
- Tool integration
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a comprehensive collection of best practices, architectural patterns, and integration guides for the Sanity ecosystem. It covers the full development lifecycle, including schema design, GROQ query optimization, TypeGen configuration, and framework-specific integrations like Next.js, Angular, and Remix. It also includes guidance on infrastructure management via Blueprints, content automation with Sanity Functions, and building custom applications with the Sanity App SDK.
What it does
- Provides standardized patterns for Sanity schema design and content modeling.
- Offers integration guides for major frontend frameworks and the Sanity App SDK.
- Defines best practices for GROQ query performance and type safety with TypeGen.
- Outlines rules for media handling, including video asset management.
- Details configuration for Sanity Studio structure and Visual Editing.
- Explains infrastructure-as-code workflows using Sanity Blueprints.
When to use it
- When starting a new Sanity project or onboarding team members.
- When integrating Sanity with frameworks like Next.js, Astro, or Angular.
- When optimizing GROQ queries or implementing type-safe data fetching.
- When designing complex content schemas or implementing localization.
- When setting up live previews, Visual Editing, or custom Studio structures.
- When automating content workflows or managing infrastructure via Blueprints.
Advantages
- Maintained by Sanity, ensuring alignment with current platform standards.
- Reduces technical debt by enforcing consistent schema and query patterns.
- Provides clear decision matrices for complex topics like video and localization.
- Includes framework-specific guidance to accelerate frontend integration.
- Promotes maintainable project structures and type-safe development workflows.
Usage examples
- “What are the best practices for handling video in Sanity?”
- The skill advises against using standard file assets for production video due to bandwidth costs, recommending the Sanity Media Library with Mux for Enterprise plans or dedicated services like Mux, YouTube, or Vimeo for others.
- “How do I set up TypeGen for an Angular monorepo?”
- The skill provides a configuration example for studio/sanity.cli.ts to point TypeGen at the Angular source files, ensuring generated types are correctly integrated into the Angular project.
- “What is the recommended way to structure singleton documents?”
- The skill recommends using explicit document IDs for singleton documents and controlling them via the Studio Structure, while letting Sanity generate IDs for all other document types.
- “How should I fetch data in a React app using the App SDK?”
- The skill mandates wrapping data-fetching components in Suspense, using documentId as a React key, and preferring useDocuments and useDocumentProjection over raw GROQ hooks.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/sanity-io/sanity-best-practices/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip sanity-best-practices.zip -d .agents/skills
Documentation by Sanity
Sanity Best Practices
Comprehensive best practices and integration guides for Sanity development, maintained by Sanity. Use the quick reference below to load only the one or two topic files that match the task.
When to Apply
Reference these guidelines when:
Setting up a new Sanity project or onboarding
Integrating Sanity with a frontend framework (Next.js, Nuxt, Astro, Remix, SvelteKit, Hydrogen)
Writing GROQ queries or optimizing performance
Designing content schemas
Implementing Visual Editing and live preview
Working with images, Portable Text, or page builders
Configuring Sanity Studio structure
Setting up TypeGen for type safety
Implementing localization
Migrating content from other systems
Building custom apps with the Sanity App SDK
Developing or deploying a Studio or app in the Sanity Dashboard
Managing infrastructure with Blueprints
Automating content workflows with Sanity Functions or webhooks
Global Rules
Let Sanity generate
_idvalues for ordinary documents. Do not create deterministic UUIDs, slug-derived IDs, or legacy-system IDs when creating documents.Model relationships with
referencefields, then resolve related documents with GROQ lookups, source-key fields, or returned_idvalues from created documents.Use explicit document IDs mainly for singleton documents controlled by Studio Structure, including localized singletons such as
homePage-en.
Video
Do not store or serve video from Sanity
fileassets for production playback. File assets are delivered as raw downloads with no transcoding or adaptive streaming, and video traffic drives very high bandwidth usage and unexpectedly large bills.On Enterprise plans with the video add-on, use Sanity Media Library for video: uploads are transcoded and streamed adaptively via Mux. Model video fields with
defineVideoField()fromsanity/media-libraryand play them with@mux/mux-player-reactusing the asset's playback ID.On other plans, use a dedicated video service: install
sanity-plugin-mux-inputto upload and manage videos in your Mux account from the Studio, or host video on a platform such as YouTube or Vimeo and store only the embed URL in Sanity.Small clips and short previews in a
filefield are acceptable, but any user-facing video at scale must go through Media Library or a streaming service.
Quick Reference
Integration Guides
get-started- Interactive onboarding for new Sanity projectsnextjs- Next.js App Router, Live Content API, standalone Studionuxt- Nuxt integration with @nuxtjs/sanityangular- Angular integration with @sanity/client, signals, resource APIastro- Astro integration with @sanity/astroremix- React Router / Remix integrationsvelte- SvelteKit integration with @sanity/svelte-loaderhydrogen- Shopify Hydrogen with Sanityproject-structure- Standalone Studio and monorepo patternsapp-sdk- Custom applications with Sanity App SDKdashboard- Studios and apps in the Sanity Dashboard: defineApplication, local dev, deploys, Dashboard hooksblueprints- Infrastructure as Code: blueprint files, stacks, plan/deploy workflow, error recovery, CI deploysfunctions- Automating content workflows with Sanity Functions and webhooks
Topic Guides
groq- GROQ query patterns, type safety, performance optimizationschema- Schema design, field definitions, validation, deprecation patternsvisual-editing- Presentation Tool, Stega, overlays, live previewpage-builder- Page Builder arrays, block components, live editingportable-text- Rich text rendering and custom componentsimage- Image schema, URL builder, hotspots, LQIP, Next.js Imagestudio-structure- Desk structure, singletons, navigationtypegen- TypeGen configuration, workflow, type utilitiesseo- Metadata, sitemaps, Open Graph, JSON-LDlocalization- i18n patterns, document vs field-level, locale managementmigration- Content import overview (see alsomigration-html-import)migration-html-import- HTML to Portable Text with @portabletext/block-tools
How to Use
Start with the single framework or topic guide that best matches the request, then read additional references only when the task crosses concerns. Use these reference files for detailed explanations and code examples:
references/groq.md
references/schema.md
references/nextjs.mdEach reference file contains:
Comprehensive topic or integration coverage
Incorrect and correct code examples
Decision matrices and workflow guidance
Framework-specific patterns where applicable
Files
- ATTRIBUTION.mdAdded by AutoGineer873 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md5.4 KiB
- references/angular.md18.6 KiB
- references/app-sdk.md12.0 KiB
- references/astro.md4.9 KiB
- references/blueprints.md14.1 KiB
- references/dashboard.md6.5 KiB
- references/functions.md29.7 KiB
- references/get-started.md21.0 KiB
- references/groq.md14.4 KiB
- references/hydrogen.md6.8 KiB
- references/image.md3.3 KiB
- references/localization.md12.0 KiB
- references/migration-html-import.md5.2 KiB
- references/migration.md2.7 KiB
- references/nextjs.md16.3 KiB
- references/nuxt.md5.9 KiB
- references/page-builder.md8.2 KiB
- references/portable-text.md9.8 KiB
- references/project-structure.md3.5 KiB
- references/remix.md8.8 KiB
- references/schema.md12.4 KiB
- references/seo.md8.3 KiB
- references/studio-structure.md4.0 KiB
- references/svelte.md10.0 KiB
- references/typegen.md6.1 KiB
- references/visual-editing.md8.4 KiB
references/angular.md
title: Angular & Sanity Integration Rules description: Integration guide for Angular, including @sanity/client setup, data fetching with signals and resource API, Portable Text rendering, and image optimization.
Angular & Sanity Integration Rules
Jump to the section that matches your Angular version or integration task instead of reading this guide straight through.
Table of Contents
Setup and configuration
Client setup (service pattern)
Data fetching patterns
Routing
Portable Text rendering
Image optimization
Modern Angular features
SSR and prerendering
Visual Editing
Error handling
1. Setup & Configuration
Use the official template sanity-template-angular-clean as a starting point. It provides a monorepo structure:
project/
├── angular-app/ # Angular 19+ frontend
└── studio/ # Sanity StudioInstall dependencies in the Angular app:
npm install @sanity/client @sanity/image-url @portabletext/to-htmlConfigure environment files for Sanity credentials:
// environments/environment.ts
export const environment = {
production: false,
sanity: {
projectId: 'your-project-id',
dataset: 'production',
apiVersion: '2025-05-01',
},
}// environments/environment.production.ts
export const environment = {
production: true,
sanity: {
projectId: 'your-project-id',
dataset: 'production',
apiVersion: '2025-05-01',
},
}There is no Angular-specific Sanity SDK. Use
@sanity/clientdirectly, wrapped in an Angular service.
TypeGen in a Monorepo
Sanity TypeGen generates TypeScript types from your schema and GROQ queries. In the Angular monorepo template, TypeGen runs from the Studio side but scans your Angular app's source files. Ensure studio/sanity.cli.ts points at the Angular app:
// studio/sanity.cli.ts
import { defineCliConfig } from 'sanity/cli'
export default defineCliConfig({
typegen: {
enabled: true,
path: '../angular-app/src/**/*.ts',
generates: '../angular-app/sanity.types.ts',
},
})The remaining defaults (overloadClientMethods: true, schema: "schema.json") work as-is. Include the generated types file in angular-app/tsconfig.json (usually covered by "include": ["src/**/*.ts", "sanity.types.ts"]). See typegen.md for the full TypeGen workflow, git strategy, and configuration options.
2. Client Setup (Service Pattern)
Create an injectable service wrapping @sanity/client and @sanity/image-url:
import { Injectable } from '@angular/core'
import { createClient, type ClientReturn, type QueryParams, type SanityClient } from '@sanity/client'
import imageUrlBuilder, { type ImageUrlBuilder } from '@sanity/image-url'
import type { SanityImageSource } from '@sanity/image-url/lib/types/types'
import { environment } from '../environments/environment'
@Injectable({ providedIn: 'root' })
export class SanityService {
private client: SanityClient
private builder: ImageUrlBuilder
constructor() {
this.client = createClient({
projectId: environment.sanity.projectId,
dataset: environment.sanity.dataset,
apiVersion: environment.sanity.apiVersion,
useCdn: true,
})
this.builder = imageUrlBuilder(this.client)
}
// ClientReturn resolves TypeGen's declaration-merged overloads for defineQuery strings
fetch<Query extends string>(query: Query, params?: QueryParams): Promise<ClientReturn<Query>> {
return this.client.fetch(query, params)
}
getImageUrlBuilder(source: SanityImageSource) {
return this.builder.image(source)
}
}For preview/draft content, create a second client instance with a token and useCdn: false. Never expose tokens in client-side bundles — use server-side rendering or a proxy endpoint for authenticated requests.
3. Data Fetching Patterns
A. resource API (Angular 19+, Recommended)
The resource API works natively with promises and integrates with Angular signals:
import { Component, input, resource, inject } from '@angular/core'
import { defineQuery } from 'groq'
import { SanityService } from '../sanity.service'
const POST_QUERY = defineQuery(`*[_type == "post" && slug.current == $slug][0]{
title, body, mainImage, publishedAt
}`)
@Component({
selector: 'app-post',
standalone: true,
template: `
@if (post.value(); as p) {
<h1>{{ p.title }}</h1>
<time>{{ p.publishedAt | date }}</time>
} @else if (post.isLoading()) {
<p>Loading…</p>
} @else if (post.error()) {
<p>Error loading post</p>
}
`,
})
export default class PostComponent {
slug = input.required<string>()
private sanity = inject(SanityService)
post = resource({
params: () => ({ slug: this.slug() }),
loader: ({ params }) => this.sanity.fetch(POST_QUERY, params),
})
}The resource automatically re-fetches when slug changes and exposes value(), isLoading(), and error() signals.
TypeGen: Wrapping queries in
defineQueryenables Sanity TypeGen to infer return types automatically — no manual type imports needed. Seetypegen.mdfor the full workflow.
B. rxResource (Observable-based)
For teams using RxJS patterns or needing operators like retry and debounceTime:
import { Component, input, inject } from '@angular/core'
import { rxResource } from '@angular/core/rxjs-interop'
import { defineQuery } from 'groq'
import { from } from 'rxjs'
import { SanityService } from '../sanity.service'
const POST_QUERY = defineQuery(`*[_type == "post" && slug.current == $slug][0]`)
@Component({ /* ... */ })
export default class PostComponent {
slug = input.required<string>()
private sanity = inject(SanityService)
post = rxResource({
params: () => ({ slug: this.slug() }),
loader: ({ params }) => from(this.sanity.fetch(POST_QUERY, params)),
})
}C. toSignal (Angular 17–18)
For apps not yet on Angular 19, convert observables to signals:
import { Component, inject } from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import { defineQuery } from 'groq'
import { from } from 'rxjs'
import { SanityService } from '../sanity.service'
const POSTS_QUERY = defineQuery(`*[_type == "post"] | order(publishedAt desc)`)
@Component({ /* ... */ })
export class HomeComponent {
private sanity = inject(SanityService)
posts = toSignal(from(this.sanity.fetch(POSTS_QUERY)), { initialValue: [] })
}Note:
toSignaldoes not re-fetch on parameter changes. For dynamic queries, useresourceorrxResource.
Choosing a pattern
| Pattern | Angular Version | Reactivity | Best For |
|---|---|---|---|
resource | 19+ | Signal-based, auto re-fetch | New projects, dynamic queries |
rxResource | 19+ | RxJS + signals | Teams using RxJS operators |
toSignal | 17+ | One-shot conversion | Static queries, legacy apps |
4. Routing
Use lazy-loaded routes with withComponentInputBinding() so route params bind directly to component inputs:
// app.config.ts
import { provideRouter, withComponentInputBinding } from '@angular/router'
import { routes } from './app.routes'
export const appConfig = {
providers: [
provideRouter(routes, withComponentInputBinding()),
],
}// app.routes.ts
import { Routes } from '@angular/router'
export const routes: Routes = [
{
path: '',
loadComponent: () => import('./home/home.component'),
pathMatch: 'full',
},
{
path: 'post/:slug',
loadComponent: () => import('./post/post.component'),
},
]With withComponentInputBinding(), the :slug route param is automatically bound to slug = input.required<string>() on the component — no need to inject ActivatedRoute.
5. Portable Text Rendering
A. @portabletext/to-html with Angular Pipe (Recommended)
import { Pipe, PipeTransform, inject } from '@angular/core'
import { toHTML, type PortableTextComponents } from '@portabletext/to-html'
import type { PortableTextBlock } from '@portabletext/types'
import { SanityService } from '../sanity.service'
@Pipe({ name: 'portableTextToHTML', standalone: true })
export class PortableTextToHTMLPipe implements PipeTransform {
private sanity = inject(SanityService)
private components: PortableTextComponents = {
types: {
image: ({ value }) => {
const url = this.sanity.getImageUrlBuilder(value).width(800).auto('format').url()
return `<img src="${url}" alt="${value.alt || ''}" loading="lazy" />`
},
},
marks: {
link: ({ children, value }) =>
`<a href="${value.href}" rel="noopener noreferrer">${children}</a>`,
},
}
transform(value: PortableTextBlock[] | undefined): string {
if (!value) return ''
return toHTML(value, { components: this.components })
}
}Usage in templates:
<div [innerHTML]="post.body | portableTextToHTML"></div>B. @limitless-angular/sanity (Community, Component-based)
For full Angular component control over each block type, the community library @limitless-angular/sanity provides a component-based Portable Text renderer. This is useful when you need Angular-specific interactivity within rich text blocks.
See portable-text.md for Portable Text schema design and serialization rules.
6. Image Optimization
Create a pipe wrapping @sanity/image-url:
import { Pipe, PipeTransform, inject } from '@angular/core'
import type { SanityImageSource } from '@sanity/image-url/lib/types/types'
import { SanityService } from '../sanity.service'
@Pipe({ name: 'sanityImage', standalone: true })
export class SanityImagePipe implements PipeTransform {
private sanity = inject(SanityService)
transform(value: SanityImageSource | undefined, width?: number): string | null {
if (!value) return null
const builder = this.sanity.getImageUrlBuilder(value)
if (width) return builder.width(width).auto('format').url()
return builder.auto('format').url()
}
}Combine with Angular's NgOptimizedImage for LCP images:
<!-- Priority image with NgOptimizedImage -->
<img [ngSrc]="post.mainImage | sanityImage: 1200" width="1200" height="630" priority />
<!-- Lazy-loaded image -->
<img [src]="post.mainImage | sanityImage: 600" [alt]="post.mainImage.alt" loading="lazy" />❌ Bad: Fetching full-size images without width constraints.
<img [src]="post.mainImage | sanityImage" />✅ Good: Specifying width and using auto('format') for WebP/AVIF delivery.
<img [src]="post.mainImage | sanityImage: 800" loading="lazy" />LQIP with NgOptimizedImage
Sanity provides a base64 LQIP (Low Quality Image Placeholder) per image asset — but you must query it explicitly:
mainImage {
// @sanity/image-url needs these to build URLs with hotspot/crop support
asset,
hotspot,
crop,
alt,
// NgOptimizedImage needs these for placeholder and layout
"lqip": asset->metadata.lqip,
"width": asset->metadata.dimensions.width,
"height": asset->metadata.dimensions.height
}Feed the LQIP directly into NgOptimizedImage's placeholder attribute:
<img
[ngSrc]="post.mainImage | sanityImage: 1200"
[width]="post.mainImage.width"
[height]="post.mainImage.height"
[placeholder]="post.mainImage.lqip"
[alt]="post.mainImage.alt"
priority
/>Angular applies a CSS blur to the LQIP and crossfades to the full image on load. No extra libraries needed.
Note: LQIP strings are small (~200 bytes) so they're safe to inline in SSR HTML and
TransferState. Seeimage.mdfor the full image query patterns.
See image.md for image field schema patterns and hotspot/crop configuration.
7. Modern Angular Features
When building with Sanity, leverage these Angular 19+ features:
Standalone components — Default in Angular 19. No
NgModuleboilerplate needed.Signals and
resource— Preferred over RxJS for data fetching. Simpler, less boilerplate.New control flow — Use
@if,@for,@switchwith@emptyfor cleaner templates:
@for (post of posts.value(); track post._id) {
<app-post-card [post]="post" />
} @empty {
<p>No posts found.</p>
}@deferblocks — Lazy-load below-fold content:
@defer (on viewport) {
<app-comments [postId]="post._id" />
} @placeholder {
<p>Scroll to see comments…</p>
}inject()function — Preferred over constructor injection for cleaner code.Zoneless change detection — Experimental in Angular 19. Works well with signals-based data fetching since signals automatically notify the framework of changes.
8. SSR & Prerendering
Angular 17+ includes built-in SSR support (replacing Angular Universal):
// app.config.server.ts
import { provideServerRendering } from '@angular/platform-server'
import { provideClientHydration } from '@angular/platform-browser'
export const serverConfig = {
providers: [
provideServerRendering(),
provideClientHydration(),
],
}Key considerations for Sanity + Angular SSR:
| Feature | Details |
|---|---|
| Hydration | provideClientHydration() preserves server-rendered DOM. The client reuses it instead of re-rendering. |
| HTTP Transfer Cache | Only works with Angular's HttpClient. Since @sanity/client uses its own HTTP transport, use TransferState manually (see below). |
| Prerendering | Use getPrerenderParams in route config to generate static pages at build time. |
Transfer State for @sanity/client
Angular's built-in HTTP Transfer Cache does not cover @sanity/client requests. Without manual transfer, the client re-fetches every query during hydration. Add TransferState to the service from Section 2:
import { Injectable, inject, PLATFORM_ID, makeStateKey, TransferState } from '@angular/core'
import { isPlatformBrowser, isPlatformServer } from '@angular/common'
import { createClient, type ClientReturn, type QueryParams, type SanityClient } from '@sanity/client'
async function hashQuery(query: string, params?: QueryParams): Promise<string> {
const input = query + JSON.stringify(params ?? {})
const buffer = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(input))
return Array.from(new Uint8Array(buffer), b => b.toString(16).padStart(2, '0')).join('')
}
export class SanityService {
private client: SanityClient
private transferState = inject(TransferState)
private platformId = inject(PLATFORM_ID)
async fetch<Query extends string>(query: Query, params?: QueryParams): Promise<ClientReturn<Query>> {
// The key type includes `null` so `get(key, null)` type-checks against
// Angular's `get<T>(key: StateKey<T>, defaultValue: T): T` signature.
const key = makeStateKey<ClientReturn<Query> | null>(await hashQuery(query, params))
if (isPlatformBrowser(this.platformId)) {
const cached = this.transferState.get(key, null)
if (cached !== null) {
this.transferState.remove(key)
return cached
}
}
const result = await this.client.fetch(query, params)
if (isPlatformServer(this.platformId)) {
this.transferState.set(key, result)
}
return result
}
}The hashQuery helper keeps TransferState keys short (SHA-256 hex) instead of embedding raw GROQ strings in the serialized HTML.
Prerendering dynamic routes:
// app.routes.server.ts
import { RenderMode, ServerRoute } from '@angular/ssr'
export const serverRoutes: ServerRoute[] = [
{
path: 'post/:slug',
renderMode: RenderMode.Prerender,
async getPrerenderParams() {
// Fetch all slugs from Sanity at build time
const client = createClient({ projectId: '...', dataset: '...', apiVersion: '...', useCdn: true })
const slugs = await client.fetch<string[]>(`*[_type == "post"].slug.current`)
return slugs.map((slug) => ({ slug }))
},
},
{ path: '**', renderMode: RenderMode.Server },
]❌ Bad: Using isPlatformBrowser() in templates to conditionally render content — causes hydration mismatch.
✅ Good: Using @defer or afterNextRender() for browser-only code.
9. Visual Editing
Important: Angular does not have official Sanity Visual Editing support. There is no
@sanity/visual-editingintegration, no Stega encoding, and no click-to-edit overlay for Angular applications. This is unlike Next.js, Nuxt, and SvelteKit which have first-party support.
Preview Mode (Basic)
For draft content preview, create a separate preview client with an API token:
@Injectable({ providedIn: 'root' })
export class SanityService {
private client: SanityClient
private previewClient: SanityClient
constructor() {
this.client = createClient({
projectId: environment.sanity.projectId,
dataset: environment.sanity.dataset,
apiVersion: environment.sanity.apiVersion,
useCdn: true,
})
this.previewClient = this.client.withConfig({
useCdn: false,
token: environment.sanity.previewToken, // Server-side only!
perspective: 'drafts',
})
}
fetch<Query extends string>(query: Query, params?: QueryParams, preview = false): Promise<ClientReturn<Query>> {
const client = preview ? this.previewClient : this.client
return client.fetch(query, params)
}
}Security: Never expose the preview token in client-side bundles. Use this pattern only with SSR where the token stays on the server, or proxy preview requests through a backend API.
Community Visual Editing
The community library @limitless-angular/sanity provides experimental Visual Editing support for Angular, including overlay click-to-edit functionality. Check its documentation for current status and limitations.
10. Error Handling
Common errors when integrating Angular with Sanity:
| Error | Cause | Solution |
|---|---|---|
401 Unauthorized | Invalid or missing API token | Verify token in Sanity Manage. Ensure it has correct permissions. |
403 Forbidden | CORS origin not allowed | Add your Angular dev/production URL to CORS origins in Sanity Manage. |
422 Invalid query | GROQ syntax error | Test queries in Vision plugin or Sanity's GROQ playground. See groq.md. |
| Hydration mismatch | Conditional rendering based on platform | Use @defer or afterNextRender() instead of isPlatformBrowser() checks. |
| Empty response | Missing dataset or wrong apiVersion | Verify environment config. Use a date-based apiVersion (e.g., '2025-05-01'). |
| Images not loading | Missing @sanity/image-url setup | Ensure getImageUrlBuilder is called with a valid image reference. See image.md. |
For GROQ query patterns and best practices, see groq.md. For schema design, see schema.md.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- Sanity
- Publisher
- Sanity (Official)
- Source
- sanity-io/agent-toolkit · skills/sanity-best-practices · commit 3ed3a14
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Sanity or Sanity.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download sanity-best-practices.zip