# Fumadocs (Framework Mode, Next.js): Sanity
URL: /zh-TW/docs/next/integrations/content/sanity
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/(framework)/[framework]/integrations/content/sanity.zh-TW.mdx

Sanity 官方整合。

## 概覽 [#overview]

Fumadocs 提供官方整合，可簡化 Sanity CMS 的設定流程。

此整合提供原生的即時預覽體驗，且不會犧牲效能。

> 您可以在 GitHub 上查看我們的 [Sanity 範例](https://github.com/fuma-nama/fumadocs-sanity)。

## 設定 [#setup]

本指南假設您已依照 [Sanity](https://sanity.io/docs/getting-started) 官方文件完成設定。

### 資料型別 [step] [#data-types]

請執行以下指令來初始化內容型別。

```npm
npx @fumadocs/cli add sanity/base
```

> 您也可以執行 `npx @fumadocs/cli add`，瀏覽並安裝其他 Sanity 預設組合。

註冊已安裝的類型：

```ts tab="schemaTypes/index.ts"
import { docsType, blockContent, callout, card, cards } from '@/lib/sanity/base';

// [!code ++]
export const schemaTypes = [blockContent, callout, card, cards, docsType];
```

```tsx tab="components/sanity.ts"
import {
  PortableText,
  type PortableTextProps,
  type PortableTextReactComponents,
} from '@portabletext/react';
// 已安裝的元件
import { baseBlocks, baseComponents, baseMarks } from './base';

const components: Partial<PortableTextReactComponents> = {
  block: {
    // [!code ++]
    ...baseBlocks,
  },
  marks: {
    // [!code ++]
    ...baseMarks,
  },
  types: {
    // [!code ++]
    ...baseComponents,
  },
};

export function CustomPortableText(props: PortableTextProps) {
  return <PortableText {...props} components={{ ...components, ...props.components }} />;
}
```

### 內容載入器 [step] [#content-loader]

安裝此整合：

```npm
npm i @fumadocs/sanity
```

若要即時預覽，請使用 `next-sanity/live` 的 `sanityFetch` 建立來源，也可以改用 Sanity 用戶端建立：

```ts title="lib/source.ts" tab="Live Preview"
import { dynamicLoader } from 'fumadocs-core/source';
import { createSanitySource } from '@fumadocs/sanity';
// 即時預覽用戶端
import { sanityFetch } from '@/sanity/lib/live';
// 產生的型別
import type { Docs } from '@/sanity/types';

const source = dynamicLoader(
  createSanitySource<Docs>({
    sanityFetch,
    docType: 'docs',
  }),
  {
    baseUrl: '/docs',
  },
);

export async function getSource() {
  return source.get();
}
```

```ts title="lib/source.ts" tab="Client"
import { dynamicLoader } from 'fumadocs-core/source';
import { createSanitySource } from '@fumadocs/sanity';
// Sanity 用戶端
import { client } from '@/sanity/lib/client';
// 產生的型別
import type { Docs } from '@/sanity/types';

const source = dynamicLoader(
  createSanitySource<Docs>({
    client,
    docType: 'docs',
  }),
  {
    baseUrl: '/docs',
  },
);

export async function getSource() {
  return source.get();
}
```

使用 `sanityFetch` 時，`<SanityLive />` 會在內容變更時重新驗證查詢，因此無須呼叫 `invalidate()`，即時更新與草稿模式即可正常運作。

若您已啟用 [Cache Components](https://nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents)，請在 `'use cache'` 範圍內 (例如經過快取的頁面) 呼叫 `getSource()`，因為 `sanityFetch` 必須在此範圍內執行。

接著使用 `getSource()` 函式算繪頁面：

```tsx title="app/docs/[[...slug]]/page.tsx"
import { notFound } from 'next/navigation';
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';
import { getSource } from '@/lib/source';
import { CustomPortableText } from '@/components/sanity';

export default async function Page(props: PageProps<'/docs/[[...slug]]'>) {
  const params = await props.params;
  const source = await getSource();
  const page = source.getPage(params.slug);
  if (!page) notFound();

  const { renderToc, body } = await page.data.load();

  return (
    <DocsPage toc={renderToc({ render: (block) => <CustomPortableText value={block} /> })}>
      <DocsTitle>{page.data.title}</DocsTitle>
      <DocsDescription>{page.data.description}</DocsDescription>
      <DocsBody>
        <CustomPortableText value={body!} />
      </DocsBody>
    </DocsPage>
  );
}
```

### 完成 [step] [#done]

在 Studio 編輯器中建立 `docs` 類型的文件後，這些文件就會出現在 docs 路由中。