Sanity
Publish documents from Sanity as a docs site.
Pages render on demand, so the site needs a server.
Setup
Set Up the Studio
Create a document type named docs, with these fields:
| Field | Type |
|---|---|
title | string (required) |
slug | slug (required) |
description | string |
body | block content (Portable Text) |
Store the Project Settings
SANITY_STUDIO_PROJECT_ID=...
SANITY_STUDIO_DATASET=...Install the Packages
npm i @fumadocs/sanity @sanity/clientCreate the Source
import { createSanitySource } from "@fumadocs/sanity";
import sanityPlugin from "@fumadocs/sanity/press";
import { createClient } from "@sanity/client";
const source = createSanitySource({
client: createClient({
projectId: process.env.SANITY_STUDIO_PROJECT_ID,
dataset: process.env.SANITY_STUDIO_DATASET,
apiVersion: "2024-12-04",
}),
docType: "docs",
});
export default createPress(source, {
alwaysRevalidate: true,
}).plugins(sanityPlugin());alwaysRevalidate shows Sanity edits right away. To refresh from a webhook instead, see Refreshing Content.
Customization
URLs
URLs come from the slug field, which can contain / for nested pages. Use baseDir to place pages under a prefix, or generatePath to set each path:
createSanitySource({
// ...
baseDir: "docs",
generatePath(doc) {
return `${doc.slug?.current ?? doc._id}.mdx`;
},
});See the Fumadocs Sanity integration for all source options.
Custom Blocks
Pass Portable Text components to render your own block types.
import { Callout } from "fumadocs-ui/components/callout";
sanityPlugin({
components: {
types: {
callout: ({ value }) => <Callout title={value.title}>{/* ... */}</Callout>,
},
},
});Drafts
To preview unpublished edits, create a second deployment that reads drafts. Its client uses the drafts perspective, which needs a token with read access and API version 2025-02-19 or later:
const preview = process.env.SANITY_PREVIEW === "true";
const source = createSanitySource({
client: createClient({
projectId: process.env.SANITY_STUDIO_PROJECT_ID,
dataset: process.env.SANITY_STUDIO_DATASET,
apiVersion: "2025-02-19",
useCdn: false,
perspective: preview ? "drafts" : "published",
token: preview ? process.env.SANITY_API_READ_TOKEN : undefined,
}),
docType: "docs",
});Set SANITY_PREVIEW and the token only on the preview deployment, and keep it private, since anyone who can open it reads your drafts. Authentication can restrict it.
Refreshing Content
The source is read once, so new content doesn't appear on its own. Choose when to read it again:
createPress(source, {
alwaysRevalidate: true,
});Reading on every request works for low traffic. On a busier site, call the webhook when your content changes.
Last updated on