• Stars
    star
    667
  • Rank 65,487 (Top 2 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created over 3 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Sanity.io toolkit for Next.js

next-sanity

Sanity.io toolkit for Next.js.

Features:

Table of contents

Installation

npm install next-sanity @portabletext/react @sanity/image-url
yarn add next-sanity @portabletext/react @sanity/image-url
pnpm install next-sanity @portabletext/react @sanity/image-url

next-sanity/studio peer dependencies

When using npm newer than v7, or pnpm newer than v8, you should end up with needed dependencies like sanity and styled-components when you npm install next-sanity. It also works in yarn v1 using install-peerdeps:

npx install-peerdeps --yarn next-sanity

next-sanity Running groq queries

import {createClient, groq} from 'next-sanity'

const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID // "pv8y60vp"
const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET // "production"
const apiVersion = process.env.NEXT_PUBLIC_SANITY_API_VERSION // "2023-05-03"

const client = createClient({
  projectId,
  dataset,
  apiVersion, // https://www.sanity.io/docs/api-versioning
  useCdn: true, // if you're using ISR or only static generation at build time then you can set this to `false` to guarantee no stale content
})

const data = await client.fetch(groq`*[]`)

appDir, React Server Components and caching

As @sanity/client will only sometimes use fetch under the hood, it depends on the environment, it's best to implement the cache function to ensure reliable caching.

import {createClient, groq} from 'next-sanity'
import {cache} from 'react'

const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID // "pv8y60vp"
const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET // "production"
const apiVersion = process.env.NEXT_PUBLIC_SANITY_API_VERSION // "2023-05-03"

const client = createClient({
  projectId,
  dataset,
  apiVersion, // https://www.sanity.io/docs/api-versioning
  useCdn: true, // if you're using ISR or only static generation at build time then you can set this to `false` to guarantee no stale content
})

// Wrap the cache function in a way that reuses the TypeScript definitions
const clientFetch = cache(client.fetch.bind(client))

// Now use it just like before, fully deduped, cached and optimized by react
const data = await clientFetch(groq`*[]`)
// You can use the same generics as before
const total = await clientFetch<number>(groq`count*()`)

next-sanity Visual Editing with Content Source Maps

Note

Content Source Maps are available as an API for select Sanity enterprise customers. Contact our sales team for more information.

The createClient method in next-sanity supports visual editing, it supports all the same options as @sanity/preview-kit/client. Add studioUrl to your client configuration and it'll automatically show up on Vercel Preview Deployments:

import {createClient, groq} from 'next-sanity'

const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID // "pv8y60vp"
const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET // "production"
const apiVersion = process.env.NEXT_PUBLIC_SANITY_API_VERSION // "2023-05-03"

const client = createClient({
  projectId,
  dataset,
  apiVersion, // https://www.sanity.io/docs/api-versioning
  useCdn: true, // if you're using ISR or only static generation at build time then you can set this to `false` to guarantee no stale content
  studioUrl: '/studio', // Or: 'https://my-cool-project.sanity.studio'
  encodeSourceMap: true, // Optional. Default to: process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview',
})

Our setup guide walks you through how to customize the experience.

next-sanity/preview Live real-time preview

You can implement real-time client side preview using definePreview. It works by streaming the whole dataset to the browser, which it keeps updated using listeners and Mendoza patches. When it receives updates, then the query is run against the client-side datastore using groq-js. It uses @sanity/preview-kit under the hood, which can be used in frameworks other than Nextjs if it supports React 18 Suspense APIs.

Examples

When running next dev locally these examples start and exit preview mode by opening localhost:3000/api/preview and localhost:3000/api/exit-preview.

Built-in Sanity auth

Pros:

  • Checks if the user is authenticated for you.
  • Pairs well with Sanity Studio preview panes.

Cons:

  • Doesn't implement a login flow:
    • Requires the user to login to a Sanity Studio prior to starting Preview mode.
    • Requires your Sanity Studio to be hosted on the same origin.
  • Currently only supports cookie based auth, and not yet the dual loginMethod in Sanity Studio:
    • Safari based browsers (Desktop Safari on Macs, and all browsers on iOS) doesn't work.
    • Doesn't support incognito browser modes.

pages/api/preview.ts:

export default function preview(req, res) {
  res.setPreviewData({})
  res.writeHead(307, {Location: '/'})
  res.end()
}

pages/api/exit-preview.ts:

export default function exit(req, res) {
  res.clearPreviewData()
  res.writeHead(307, {Location: '/'})
  res.end()
}

components/DocumentsCount.tsx:

import groq from 'groq'

export const query = groq`count(*[])`

export function DocumentsCount({data}) {
  return (
    <>
      Documents: <strong>{data}</strong>
    </>
  )
}

lib/sanity.client.ts

import {createClient} from 'next-sanity'

const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID // "pv8y60vp"
const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET // "production"
const apiVersion = process.env.NEXT_PUBLIC_SANITY_API_VERSION // "2023-05-03"

export const client = createClient({projectId, dataset, apiVersion})

lib/sanity.preview.ts

import {definePreview} from 'next-sanity/preview'
import {projectId, dataset} from 'lib/sanity.client'

function onPublicAccessOnly() {
  throw new Error(`Unable to load preview as you're not logged in`)
}
export const usePreview = definePreview({projectId, dataset, onPublicAccessOnly})

components/PreviewDocumentsCount.tsx:

'use client'

import {usePreview} from 'lib/sanity.preview'
import {query, DocumentsCount} from 'components/DocumentsCount'

export default function PreviewDocumentsCount() {
  const data = usePreview(null, query)
  return <DocumentsCount data={data} />
}
Using the /pages directory

pages/index.tsx:

import {PreviewSuspense} from 'next-sanity/preview'
import {lazy} from 'react'
import {DocumentsCount, query} from 'components/DocumentsCount'
import {client} from 'lib/sanity.client'

const PreviewDocumentsCount = lazy(() => import('components/PreviewDocumentsCount'))

export const getStaticProps = async ({preview = false}) => {
  if (preview) {
    return {props: {preview}}
  }

  const data = await client.fetch(query)

  return {props: {preview, data}}
}

export default function IndexPage({preview, data}) {
  if (preview) {
    return (
      <PreviewSuspense fallback="Loading...">
        <PreviewDocumentsCount />
      </PreviewSuspense>
    )
  }

  return <DocumentsCount data={data} />
}
Using the /app directory

components/PreviewSuspense.tsx:

'use client'

// Once rollup supports 'use client' module directives then 'next-sanity' will include them and this re-export will no longer be necessary
export {PreviewSuspense as default} from 'next-sanity/preview'

app/page.tsx:

import {previewData} from 'next/headers'
import PreviewSuspense from 'components/PreviewSuspense'
import {DocumentsCount, query} from 'components/DocumentsCount'
import PreviewDocumentsCount from 'components/PreviewDocumentsCount'
import {client} from 'lib/sanity.client'
import {cache} from 'react'

// Enable NextJS to cache and dedupe queries
const clientFetch = cache(client.fetch.bind(client))

export default async function IndexPage() {
  if (previewData()) {
    return (
      <PreviewSuspense fallback="Loading...">
        <PreviewDocumentsCount />
      </PreviewSuspense>
    )
  }

  const data = await clientFetch(query)
  return <DocumentsCount data={data} />
}

Custom token auth

By providing a read token (Sanity API token with viewer rights) you override the built-in auth and get more control and flexibility.

Pros:

  • Allows launching previews for users without necessarily an Sanity account.
  • Hosting a Sanity Studio on the same origin is optional.
  • Can build preview experiences that start outside a Studio, like "Copy share link" functionality.
  • Works in all Safari based browsers (Desktop Safari on Macs, and all browsers on iOS).
  • Works with incognito browser modes.

Cons:

  • Like all things with great power comes great responsibility. You're responsible for implementing adequate protection against leaking the token in your js bundle, or preventing the /api/preview?secret=${secret} from being easily guessable.
  • It results in a larger JS bundle as @sanity/groq-store currently requires event-source-polyfill since native window.EventSource does not support setting Authorization headers needed for the token auth.

pages/api/preview.ts:

import getSecret from 'lib/getSecret'

export default async function preview(req, res) {
  // The secret can't be stored in an env variable with a NEXT_PUBLIC_ prefix, as it would make you vulnerable to leaking the token to anyone.
  // If you don't have an custom API with authentication that can handle checking secrets, you may use https://github.com/sanity-io/sanity-studio-secrets to store the secret in your dataset.
  const secret = await getSecret()

  // This is the most common way to check for auth, but we encourage you to use your existing auth infra to protect your token and securely transmit it to the client
  if (!req.query.secret || req.query.secret !== secret) {
    return res.status(401).json({message: 'Invalid secret'})
  }

  res.setPreviewData({token: process.env.SANITY_API_READ_TOKEN})
  res.writeHead(307, {Location: '/'})
  res.end()
}

pages/api/exit-preview.ts:

export default function exit(req, res) {
  res.clearPreviewData()
  res.writeHead(307, {Location: '/'})
  res.end()
}

components/DocumentsCount.tsx:

import groq from 'groq'

export const query = groq`count(*[])`

export function DocumentsCount({data}) {
  return (
    <>
      Documents: <strong>{data}</strong>
    </>
  )
}

lib/sanity.client.ts

import {createClient} from 'next-sanity'

const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID // "pv8y60vp"
const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET // "production"
const apiVersion = process.env.NEXT_PUBLIC_SANITY_API_VERSION // "2023-05-03"

export const client = createClient({projectId, dataset, apiVersion})

lib/sanity.preview.ts

import {definePreview} from 'next-sanity/preview'
import {projectId, dataset} from 'lib/sanity.client'

export const usePreview = definePreview({projectId, dataset})

components/PreviewDocumentsCount.tsx:

'use client'

import {usePreview} from 'lib/sanity.preview'
import {query, DocumentsCount} from 'components/DocumentsCount'

export default function PreviewDocumentsCount({token}) {
  const data = usePreview(token, query)
  return <DocumentsCount data={data} />
}
Using the /pages directory

pages/index.tsx:

import {PreviewSuspense} from 'next-sanity/preview'
import {lazy} from 'react'
import {DocumentsCount, query} from 'components/DocumentsCount'
import {client} from 'lib/sanity.client'

// Wrapping preview components in React.lazy ensures visitors not on preview mode doesn't load any JS related to it
const PreviewDocumentsCount = lazy(() => import('components/PreviewDocumentsCount'))

export const getStaticProps = async ({preview = false, previewData = {}}) => {
  if (preview && previewData?.token) {
    return {props: {preview, token: previewData.token}}
  }

  const data = await client.fetch(query)

  return {props: {preview, data}}
}

export default function IndexPage({preview, token, data}) {
  if (preview) {
    return (
      <PreviewSuspense fallback="Loading...">
        <PreviewDocumentsCount token={token} />
      </PreviewSuspense>
    )
  }

  return <DocumentsCount data={data} />
}
Using the /app directory

components/PreviewSuspense.tsx:

'use client'

// Once rollup supports 'use client' module directives then 'next-sanity' will include them and this re-export will no longer be necessary
export {PreviewSuspense as default} from 'next-sanity/preview'

app/page.tsx:

import {previewData} from 'next/headers'
import PreviewSuspense from 'components/PreviewSuspense'
import {DocumentsCount, query} from 'components/DocumentsCount'
import PreviewDocumentsCount from 'components/PreviewDocumentsCount'
import {client} from 'lib/sanity.client'

type AppPreviewData = {token: string} | undefined
export default async function IndexPage() {
  if ((previewData() as AppPreviewData)?.token) {
    return (
      <PreviewSuspense fallback="Loading...">
        <PreviewDocumentsCount token={(previewData() as AppPreviewData).token} />
      </PreviewSuspense>
    )
  }

  const data = await client.fetch(query)
  return <DocumentsCount data={data} />
}

Starters

Limits

The real-time preview isn't optimized and comes with a configured limit of 3000 documents. You can experiment with larger datasets by configuring the hook with documentLimit: <Integer>. Be aware that this might significantly affect the preview performance. You may use the includeTypes option to reduce the amount of documents and reduce the risk of hitting the documentLimit:

import {definePreview} from 'next-sanity/preview'

export const usePreview = definePreview({
  projectId,
  dataset,
  documentLimit: 10000,
  includeTypes: ['page', 'product', 'sanity.imageAsset'],
  // If you have a lot of editors changing content at the same time it might help to increase this value
  // to reduce the amount of rerenders React have to perform.
  subscriptionThrottleMs: 300,
})

We have plans for optimizations in the roadmap.

next-sanity/studio

See it live

The latest version of Sanity Studio allows you to embed a near-infinitely configurable content editing interface into any React application. This opens up many possibilities:

  • Any service that hosts Next.js apps can now host your Studio.
  • Building previews for your content is easier as your Studio lives in the same environment.
  • Use Data Fetching to configure your Studio.
  • Easy setup of Preview Mode.

Usage

NextStudio loads up the import {Studio} from 'sanity' component for you and wraps it in a Next-friendly layout. metadata specifies the necessary <meta> tags for making the Studio adapt to mobile devices, and prevents the route from being indexed by search engines.

Both the Next /app and /pages examples uses this config file: sanity.config.ts:

import {defineConfig} from 'sanity'
import {deskTool} from 'sanity/desk'

import {schemaTypes} from './schemas'

const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!
const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET!

export default defineConfig({
  basePath: '/studio', // <-- important that `basePath` matches the route you're mounting your studio from, it applies to both `/pages` and `/app`

  projectId,
  dataset,

  plugins: [deskTool()],

  schema: {
    types: schemaTypes,
  },
})

To use sanity.cli.ts with the same projectId and dataset as your sanity.config.ts:

/* eslint-disable no-process-env */
import {loadEnvConfig} from '@next/env'
import {defineCliConfig} from 'sanity/cli'

const dev = process.env.NODE_ENV !== 'production'
loadEnvConfig(__dirname, dev, {info: () => null, error: console.error})

const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID
const dataset = process.env.NEXT_PUBLIC_SANITY_DATASET

export default defineCliConfig({api: {projectId, dataset}})

Now you can run commands like npx sanity cors add. See npx sanity help for a full list of what you can do.

Using the /app directory

We support the new appDir mode in Next.

app/studio/[[...index]]/page.tsx:

import {Studio} from './Studio'

// Set the right `viewport`, `robots` and `referer` meta tags
export {metadata} from 'next-sanity/studio/metadata'

export default function StudioPage() {
  return <Studio />
}

app/studio/[[...index]]/Studio.tsx:

'use client'

import {NextStudio} from 'next-sanity/studio'

import config from '../../../sanity.config'

export function Studio() {
  //  Supports the same props as `import {Studio} from 'sanity'`, `config` is required
  return <NextStudio config={config} />
}

Customize meta tags app/studio/[[...index]]/page.tsx:

import type {Metadata} from 'next'
import {metadata as studioMetadata} from 'next-sanity/studio/metadata'

import {Studio} from './Studio'

// Set the right `viewport`, `robots` and `referer` meta tags
export const metadata: Metadata = {
  ...studioMetadata,
  // Overrides the viewport to resize behavior
  viewport: `${studioMetadata.viewport}, interactive-widget=resizes-content`,
}

export default function StudioPage() {
  return <Studio />
}

Using the /pages directory

/pages/studio/[[...index]].tsx:

import Head from 'next/head'
import {NextStudio} from 'next-sanity/studio'
import {metadata} from 'next-sanity/studio/metadata'

import config from '../../sanity.config'

export default function StudioPage() {
  return (
    <>
      <Head>
        {Object.entries(metadata).map(([key, value]) => (
          <meta key={key} name={key} content={value} />
        ))}
      </Head>
      <NextStudio config={config} />
    </>
  )
}

Opt-in to using StudioProvider and StudioLayout

If you want to go lower level and have more control over the studio you can pass StudioProvider and StudioLayout from sanity as children:

import {NextStudio} from 'next-sanity/studio'
import {StudioProvider, StudioLayout} from 'sanity'

import config from '../../../sanity.config'

function StudioPage() {
  return (
    <NextStudio config={config}>
      <StudioProvider config={config}>
        {/* Put components here and you'll have access to the same React hooks as Studio gives you when writing plugins */}
        <StudioLayout />
      </StudioProvider>
    </NextStudio>
  )
}

next-sanity/webhook

Implements @sanity/webhook to parse and verify that a Webhook is indeed coming from Sanity infrastructure.

pages/api/revalidate:

import type {NextApiRequest, NextApiResponse} from 'next'
import {parseBody} from 'next-sanity/webhook'

// Export the config from next-sanity to enable validating the request body signature properly
export {config} from 'next-sanity/webhook'

export default async function revalidate(req: NextApiRequest, res: NextApiResponse) {
  try {
    const {isValidSignature, body} = await parseBody(req, process.env.SANITY_REVALIDATE_SECRET)

    if (!isValidSignature) {
      const message = 'Invalid signature'
      console.warn(message)
      res.status(401).json({message})
      return
    }

    const staleRoute = `/${body.slug.current}`
    await res.revalidate(staleRoute)
    const message = `Updated route: ${staleRoute}`
    console.log(message)
    return res.status(200).json({message})
  } catch (err) {
    console.error(err)
    return res.status(500).json({message: err.message})
  }
}

Migrate

From v2

The v3 release only contains breaking changes on the next-sanity/studio imports. If you're only using import {createClient, groq} from 'next-sanity' or import {definePreview, PreviewSuspense} from 'next-sanity/preview' then there's no migration for you to do.

NextStudioGlobalStyle is removed

The layout is no longer using global CSS to set the Studio height. The switch to local CSS helps interop between Next /pages and /app layouts.

ServerStyleSheetDocument is removed

It's no longer necessary to setup styled-components SSR for the Studio to render correctly.

The internal isWorkspaceWithTheme and isWorkspaces utils are no longer exported

The useTheme hook is still available if you're building abstractions that need to know what the initial workspace theme variables are.

The useBackgroundColorsFromTheme, useBasePath, useConfigWithBasePath, and useTextFontFamilyFromTheme, hooks are removed

You can useTheme to replace useBackgroundColorsFromTheme and useTextFontFamilyFromTheme:

import {useMemo} from 'react'
import {useTheme} from 'next-sanity/studio'
import type {StudioProps} from 'sanity'
export default function MyComponent(props: Pick<StudioProps, 'config'>) {
  const theme = useTheme(config)
  // useBackgroundColorsFromTheme
  const {themeColorLight, themeColorDark} = useMemo(
    () => ({
      themeColorLight: theme.color.light.default.base.bg,
      themeColorDark: theme.color.dark.default.base.bg,
    }),
    [theme]
  )
  // useTextFontFamilyFromTheme
  const fontFamily = useMemo(() => theme.fonts.text.family, [theme])
}

The reason why useBasePath and useConfigWithBasePath got removed is because Next /pages and /app diverge too much in how they declare dynamic segments. Thus you'll need to specify basePath in your sanity.config.ts manually to match the route you're loading the studio, for the time being.

The NextStudioHead component has moved from next-sanity/studio to next-sanity/studio/head

Its props are also quite different and it now requires you to wrap it in import Head from 'next/head' if you're not using a head.tsx in appDir. Make sure you use TypeScript to ease the migration.

From v1

createPreviewSubscriptionHook is replaced with definePreview

There are several differences between the hooks. First of all, definePreview requires React 18 and Suspense. And as it's designed to work with React Server Components you provide token in the hook itself instead of in the definePreview step. Secondly, definePreview encourages code-splitting using React.lazy and that means you only call the usePreview hook in a component that is lazy loaded. Quite different from usePreviewSubscription which was designed to be used in both preview mode, and in production by providing initialData.

Before

The files that are imported here are the same as the Next /pages example.

pages/index.tsx

import {createPreviewSubscriptionHook} from 'next-sanity'
import {DocumentsCount, query} from 'components/DocumentsCount'
import {client, projectId, dataset} from 'lib/sanity.client'

export const getStaticProps = async ({preview = false}) => {
  const data = await client.fetch(query)

  return {props: {preview, data}}
}

const usePreviewSubscription = createPreviewSubscriptionHook({projectId, dataset})
export default function IndexPage({preview, data: initialData}) {
  const {data} = usePreviewSubscription(indexQuery, {initialData, enabled: preview})
  return <DocumentsCount data={data} />
}
After

components/PreviewDocumentsCount.tsx

import {definePreview} from 'next-sanity/preview'
import {projectId, dataset} from 'lib/sanity.client'

const usePreview = definePreview({projectId, dataset})
export default function PreviewDocumentsCount() {
  const data = usePreview(null, query)
  return <DocumentsCount data={data} />
}

pages/index.tsx

import {lazy} from 'react'
import {PreviewSuspense} from 'next-sanity/preview'
import {DocumentsCount, query} from 'components/DocumentsCount'
import {client} from 'lib/sanity.client'

const PreviewDocumentsCount = lazy(() => import('components/PreviewDocumentsCount'))

export const getStaticProps = async ({preview = false}) => {
  const data = await client.fetch(query)

  return {props: {preview, data}}
}

export default function IndexPage({preview, data}) {
  if (preview) {
    return (
      <PreviewSuspense fallback={<DocumentsCount data={data} />}>
        <PreviewDocumentsCount />
      </PreviewSuspense>
    )
  }
  return <DocumentsCount data={data} />
}

createCurrentUserHook is removed

If you used this hook to check if the user is cookie authenticated:

import {createCurrentUserHook} from 'next-sanity'

const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID
const useCurrentUser = createCurrentUserHook({projectId})
const useCheckAuth = () => {
  const {data, loading} = useCurrentUser()
  return loading ? false : !!data
}

export default function Page() {
  const isAuthenticated = useCheckAuth()
}

Then you can achieve the same functionality using @sanity/preview-kit and suspend-react:

import {suspend} from 'suspend-react'
import {_checkAuth} from '@sanity/preview-kit'

const projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID
const useCheckAuth = () =>
  suspend(() => _checkAuth(projectId, null), ['@sanity/preview-kit', 'checkAuth', projectId])

export default function Page() {
  const isAuthenticated = useCheckAuth()
}

From v0.4

createPortableTextComponent is removed

This utility used to wrap @sanity/block-content-to-react. It's encouraged to upgrade to @portabletext/react.

$ npm install @portabletext/react
// or
$ yarn add @portabletext/react
-import { createPortableTextComponent } from 'next-sanity'
+import { PortableText as PortableTextComponent } from '@portabletext/react'

-export const PortableText = createPortableTextComponent({ serializers: {} })
+export const PortableText = (props) => <PortableTextComponent components={{}} {...props} />

Please note that the serializers and components are not 100% equivalent.

Check the full migration guide.

createImageUrlBuilder is removed

This utility is no longer wrapped by next-sanity and you'll need to install the dependency yourself:

$ npm install @sanity/image-url
// or
$ yarn add @sanity/image-url
-import { createImageUrlBuilder } from 'next-sanity'
+import createImageUrlBuilder from '@sanity/image-url'

Release new version

Run "CI & Release" workflow. Make sure to select the main branch and check "Release new version".

Semantic release will only release on configured branches, so it is safe to run release on any branch.

License

MIT-licensed. See LICENSE.

More Repositories

1

sanity

Sanity Studio – Rapidly configure content workspaces powered by structured content
TypeScript
4,364
star
2

litter

Litter is a pretty printer library for Go data structures to aid in debugging and testing.
Go
1,437
star
3

nextjs-blog-cms-sanity-v3

A Next.js Blog with a Native Authoring Experience
TypeScript
376
star
4

GROQ

Specification for GROQ - The Query Language for JSON
JavaScript
368
star
5

hydrogen-sanity-demo

A starter for Hydrogen + Sanity projects
TypeScript
312
star
6

example-company-website-gatsby-sanity-combo

This is an example company website using Gatsby and Sanity in combination.
JavaScript
270
star
7

groq-js

JavaScript implementation of GROQ, the query language for JSON
TypeScript
250
star
8

mendoza

Differ for structured documents (JSON)
Go
234
star
9

example-frontend-next-js

An example of a Sanity powered frontend using Next.js
JavaScript
212
star
10

gatsby-source-sanity

Gatsby source plugin for building websites using Sanity.io as a backend.
TypeScript
195
star
11

groq-cli

Run GROQ in your command line
JavaScript
193
star
12

template-nextjs-personal-website

A Next.js Personal Website with a Native Authoring Experience
TypeScript
165
star
13

block-content-to-react

Deprecated in favor of @portabletext/react
JavaScript
162
star
14

sanity-recipes

A collection of recipies / snippets / frequently asked questions about Sanity.io
JavaScript
159
star
15

sanity-plugin-media

Asset management plugin for Sanity
TypeScript
147
star
16

sanity-template-nextjs-landing-pages

A Sanity powered landing page builder in Next.js for https://sanity.io/create
JavaScript
142
star
17

squizzy

Quizzes with Squizzy the Squid!
JavaScript
139
star
18

sanity-shopify-studio

An example Sanity Studio configured for headless Shopify projects.
TypeScript
134
star
19

tutorial-sanity-blog-react-next

Front-end code for the Sanity, React, Next.js tutorial.
TypeScript
133
star
20

document-internationalization

Create unique translations of a document based on its language, joined by a shared reference document
TypeScript
118
star
21

sanity-template-nextjs-clean

A clean Next.js template with a native authoring experience
TypeScript
117
star
22

ui

UI building blocks for Sanity.
TypeScript
117
star
23

sanity-template-nextjs-app-router-personal-website

A Next.js Personal Website with a Native Authoring Experience. Uses app router.
113
star
24

sanity-template-kitchen-sink

A collection of demo examples
JavaScript
103
star
25

preview-kit

General purpose live previews, like next-sanity
TypeScript
100
star
26

example-ecommerce-snipcart-vue

The Transglobal Candy Store: Sample front-end for the Sanity.io e-commerce schema with vue.js, nuxt.js, and snipcart
Vue
100
star
27

sanity-template-gatsby-blog

A Sanity powered Gatsby blog for https://www.sanity.io/create
JavaScript
99
star
28

sanity-template-nextjs-blog-comments

JavaScript
83
star
29

sanity-template-nextjs-ecommerce

CSS
76
star
30

example-frontend-vue-js

An example of a Sanity powered frontend using Vue.js
Vue
72
star
31

community-studio

Sanity Community Management Studio
TypeScript
72
star
32

sanity-template-astro-clean

Clean starter template with Astro
Astro
70
star
33

sanity-template-gatsby-portfolio

A Gatsby portfolio site powered by Sanity for https://www.sanity.io/create
JavaScript
70
star
34

client

JavaScript client for retrieving, creating and patching data from Sanity.io
TypeScript
69
star
35

content-source-maps

Specification for the Content Source Maps standard
69
star
36

orderable-document-list

Drag-and-drop Document Ordering without leaving the Editing surface
TypeScript
67
star
37

plugin-kit

Enhanced Sanity.io plugin development experience.
TypeScript
64
star
38

sanity-algolia

Utilities for indexing Sanity documents in Algolia
TypeScript
61
star
39

sanity-astro

Astro
60
star
40

github-action-sanity

Dockerfile
52
star
41

sanity-plugin-graph-view

A tool for Sanity Studio to graph your content and see changes in real-time.
TypeScript
49
star
42

demo-course-platform

An example Studio and Front End demonstrating different strategies for localization with Sanity.io
TypeScript
49
star
43

next-recipe-app

CSS
47
star
44

sanity-template-nuxt-events

A Sanity powered Conference site in Vue / Nuxt for https://www.sanity.io/create
Vue
46
star
45

startup-starter-kit

The Structured Content Startup Starter Kit
JavaScript
45
star
46

netlify-form-sanity

How to use Netlify Forms and Functions to submit data to Sanity.io
HTML
45
star
47

sanity-plugin-markdown

Markdown support in the Sanity Studio
TypeScript
45
star
48

demo-custom-workflow

A demonstration of a custom content publishing workflow using Sanity.
HTML
44
star
49

sanity-template-sapper-blog

JavaScript
44
star
50

sanity-plugin-iframe-pane

Display any URL in a View Pane, along with helpful buttons to copy the URL, display a mobile size, reload the iframe or open in a new tab
TypeScript
44
star
51

image-url

Tools to generate image urls from Sanity content
TypeScript
43
star
52

sanity-plugin-mux-input

An input component that integrates Sanity Studio with MUX.com video encoding/hosting service.
TypeScript
41
star
53

sanity-template-nuxt-clean

Clean starter template with Nuxt
Vue
41
star
54

gatsby-portfolio-preview-poc

Gatsby Portfolio Preview POC
JavaScript
40
star
55

groq-store

In-memory GROQ store. Streams all available documents from Sanity into an in-memory database for local querying.
TypeScript
40
star
56

demo-marketing-site-nextjs

TypeScript
39
star
57

sanity-template-nextjs-event-starter

Fully customizable starter kit for your virtual event.
TypeScript
39
star
58

sanity-plugin-seo-pane

Run Yoast's SEO review tools using Sanity data, inside a List View Pane.
TypeScript
38
star
59

vscode-sanity

Visual Studio Code extension for developing applications powered by Sanity.io
TypeScript
38
star
60

example-app-react-native

Sanity + React Native app example
JavaScript
38
star
61

sanity-template-eleventy-blog

Minimal blog with Eleventy and Sanity
JavaScript
37
star
62

hierarchical-document-list

Plugin for editing hierarchical references in the Sanity studio.
TypeScript
37
star
63

sanity-plugin-internationalized-array

A plugin to register array fields with a custom input component to store field values in multiple languages, queryable by using the language ID as an array `_key`.
TypeScript
37
star
64

sanity-studio-secrets

Hooks and chrome for handling secrets in plugins
TypeScript
36
star
65

react-rx

React + RxJS = <3
TypeScript
36
star
66

mendoza-js

Mendoza decoder in TypeScript
TypeScript
35
star
67

themer

Experimental, creates Studio v3 themes
TypeScript
34
star
68

table

Table schema type and input component for Sanity Studio
TypeScript
34
star
69

webhook-toolkit

Toolkit for dealing with GROQ-powered webhooks delivered by Sanity.io
TypeScript
34
star
70

sanity-template-sveltekit-clean

Clean starter template with SvelteKit
CSS
32
star
71

sanity-template-svelte-kit

A minimal, fully customizable SvelteKit front-end powered by Sanity.io data.
JavaScript
32
star
72

swift-sanity

Swift
31
star
73

sanity-plugin-dashboard-widget-vercel

TypeScript
31
star
74

sanity-plugin-scheduled-publishing

Schedule documents for future publishing
TypeScript
29
star
75

hydrogen-sanity

TypeScript
29
star
76

sanity-plugin-dashboard-widget-netlify

Sanity Studio Dashboard Widget for triggering Netlify builds
TypeScript
29
star
77

sanity-template-vercel-visual-editing

TypeScript
29
star
78

cross-dataset-duplicator

Empower content editors to migrate Documents and Assets between Sanity Projects and Datasets from inside Sanity Studio.
TypeScript
29
star
79

sanity-php

PHP library for retrieving, creating and patching data from Sanity.io
PHP
28
star
80

get-it

Composable HTTP request library for node and browsers
TypeScript
26
star
81

contentful-to-sanity

Migrate from Contentful to Sanity
TypeScript
26
star
82

sanity-template-gridsome-blog

A Sanity powered Gridsome blog for sanity.io/create
JavaScript
25
star
83

visual-editing

TypeScript
25
star
84

asset-utils

Reusable utility functions for dealing with image and file assets in Sanity
TypeScript
24
star
85

sanity-template-remix-clean

Clean starter template with Remix
TypeScript
23
star
86

jsonwebtoken-esm

jsonwebtoken wrapper that provides esm support
JavaScript
22
star
87

create-react-app-blog

JavaScript
22
star
88

pkg-utils

Simple utilities for modern npm packages.
TypeScript
22
star
89

demo-media-site-nextjs

A demo template for a content-driven site with longform content and newsletter capability
TypeScript
21
star
90

sanity-mux-player

Play videos in the frontend uploaded with the MUX Sanity plugin
JavaScript
21
star
91

sanity-plugin-hotspot-array

A configurable Custom Input for Arrays that will add and update items by clicking on an Image
TypeScript
21
star
92

dashboard

Tool for rendering dashboard widgets
TypeScript
21
star
93

sanity-nextjs-vercel-example

A bare bones example of a Vercel-deployable project with a Next.js frontend and a Sanity Studio on /studio
JavaScript
21
star
94

block-content-to-html

Deprecated in favor of @portabletext/to-html
JavaScript
21
star
95

block-content-to-markdown

Transform Sanity block content to Markdown
JavaScript
20
star
96

sanity-plugin-cloudinary

Official plugin for integrating Sanity Studio with Cloudinary
TypeScript
20
star
97

demo-ecommerce

TypeScript
20
star
98

structured-content-2022

TypeScript
20
star
99

locales

A repository of user-contributed locale/language packs for Sanity Studio
TypeScript
20
star
100

gridsome-source-sanity

Sanity source plugin for Gridsome
JavaScript
19
star