---
title: "React"
description: "学习如何将 Tiptap 编辑器与 React 应用集成，并开发自定义编辑器体验。"
canonical_url: "https://tiptap.zhcndoc.com/editor/getting-started/install/react"
---

# React

学习如何将 Tiptap 编辑器与 React 应用集成，并开发自定义编辑器体验。

本指南描述了如何将 Tiptap 与您的 React 项目集成。我们使用 Vite，但其他设置的工作流程应该类似。

> **Interactive demo:** [Default](https://embed.tiptap.dev/preview/Examples/Default)

## 创建一个 React 项目（可选）

以一个名为 `my-tiptap-project` 的新 React 项目开始。 [Vite](https://vitejs.dev/guide/) 将设置我们所需的一切。

```bash
# 使用 npm 创建项目
npm create vite@latest my-tiptap-project -- --template react-ts

# 或者，使用 pnpm 创建项目
pnpm create vite@latest my-tiptap-project --template react-ts

# 或者，使用 yarn 创建项目
yarn create vite my-tiptap-project --template react-ts

# 进入目录
cd my-tiptap-project
```

## 安装依赖

接下来，安装 `@tiptap/react` 包，`@tiptap/pm`（ProseMirror 库）和 `@tiptap/starter-kit`，它包括启动时常用的扩展。

- **@tiptap/react**：Tiptap 的 React 绑定，包括 Tiptap 的核心功能。
- **@tiptap/pm**：Tiptap 的 ProseMirror 依赖，是编辑器正常工作的必需库。
- **@tiptap/starter-kit**：一组常用扩展，提供段落、标题、加粗、斜体等基本功能。

```bash
npm install @tiptap/react @tiptap/pm @tiptap/starter-kit
```

如果您按照步骤 1 和 2 操作，现在可以通过 `npm run dev` 启动项目，并在浏览器中打开 [http://localhost:3000](http://localhost:3000)。

## 集成 Tiptap

> **New: React Composable API:**
>
> Tiptap 现在提供了一个声明式的 `<Tiptap>` 组件，具有自动上下文管理和内置子组件。非常适合拥有多个子组件的复杂 UI。[了解更多 →](https://tiptap.zhcndoc.com/guides/react-composable-api.md)

要开始使用 Tiptap，创建一个新组件。我们称之为 `Tiptap`，并在 `src/Tiptap.tsx` 中添加以下代码：

```jsx
// src/Tiptap.tsx
import { useEditor, EditorContent } from '@tiptap/react'
import { FloatingMenu, BubbleMenu } from '@tiptap/react/menus'
import StarterKit from '@tiptap/starter-kit'

const Tiptap = () => {
  const editor = useEditor({
    extensions: [StarterKit], // 定义您的扩展数组
    content: '<p>Hello World!</p>', // 初始内容
  })

  return (
    <>
      <EditorContent editor={editor} />
      <FloatingMenu editor={editor}>这是浮动菜单</FloatingMenu>
      <BubbleMenu editor={editor}>这是气泡菜单</BubbleMenu>
    </>
  )
}

export default Tiptap
```

### 将其添加到您的应用中

最终，替换 `src/App.tsx` 的内容为新的 `Tiptap` 组件。

```jsx
import Tiptap from './Tiptap'

const App = () => {
  return (
    <div className="card">
      <Tiptap />
    </div>
  )
}

export default App
```

## 在子组件中消费编辑器上下文

Tiptap 提供了一个名为 `EditorContext` 的 React 上下文，允许您在组件树的任何位置访问编辑器实例及其状态。这对于构建自定义工具栏、菜单或其他需要与编辑器交互的组件非常有用。

```jsx
// src/Tiptap.tsx
import { useEditor, EditorContent, EditorContext } from '@tiptap/react'
import { FloatingMenu, BubbleMenu } from '@tiptap/react/menus'
import StarterKit from '@tiptap/starter-kit'
import { useMemo } from 'react'

const Tiptap = () => {
  const editor = useEditor({
    extensions: [StarterKit], // 定义扩展数组
    content: '<p>Hello World!</p>', // 初始内容
  })

  // 缓存 Provider 的值，避免不必要的重新渲染
  const providerValue = useMemo(() => ({ editor }), [editor])

  return (
    <EditorContext.Provider value={providerValue}>
      <EditorContent editor={editor} />
      <FloatingMenu editor={editor}>这是浮动菜单</FloatingMenu>
      <BubbleMenu editor={editor}>这是气泡菜单</BubbleMenu>
    </EditorContext.Provider>
  )
}

export default Tiptap
```

### 在子组件中使用编辑器上下文

如果您使用 `EditorContext.Provider` 来设置 Tiptap 编辑器，现在可以在任意子组件中通过 `useCurrentEditor` 钩子访问编辑器实例。

```tsx
import { useCurrentEditor } from '@tiptap/react'

const EditorJSONPreview = () => {
  const { editor } = useCurrentEditor()

  return <pre>{JSON.stringify(editor.getJSON(), null, 2)}</pre>
}
```

**重要**：如果您使用 `useEditor` 钩子来设置您的编辑器，则此方法无效。

您现在应该在浏览器中看到一个非常基础的 Tiptap 示例。

### 添加开始或结束插槽

由于 `EditorContent` 组件是由 `EditorProvider` 组件渲染的，因此我们现在无法直接定义在编辑器内容之前或之后渲染的位置。为此，我们可以在 `EditorProvider` 组件上使用 `slotBefore` 和 `slotAfter` 属性。

```tsx
<EditorProvider
  extensions={extensions}
  content={content}
  slotBefore={<MyEditorToolbar />}
  slotAfter={<MyEditorFooter />}
/>
```

### 容器属性

`EditorProvider` 组件接受一个 `editorContainerProps` 属性来传递属性到编辑器提供器的容器元素。

```tsx
<EditorProvider
  extensions={extensions}
  content={content}
  editorContainerProps={{ className: 'editor-container' }}
/>
```

## 响应编辑器状态变化

要响应编辑器状态变化，您可以使用 `@tiptap/react` 提供的 `useEditorState` 钩子。该钩子可用于从编辑器状态中获取信息，而不会导致编辑器组件或其子组件重新渲染。

```jsx
import { useEditorState } from '@tiptap/react'

function MyEditorComponent() {
  // ... 您的编辑器设置代码

  const editorState = useEditorState({
    editor,

    // selector 函数用于选择您想要响应的状态
    selector: ({ editor }) => {
      if (!editor) return null;

      return {
        isEditable: editor.isEditable,
        currentSelection: editor.state.selection,
        currentContent: editor.getJSON(),
        // 您可以添加更多状态属性，例如：
        // isBold: editor.isActive('bold'),
        // isItalic: editor.isActive('italic'),
      };
    },
  });
}
```

> **提示:**
>
> 使用 `<Tiptap>` 组件时，优先使用 `useTiptapState`，它会自动使用上下文中的编辑器实例。

## 在 React 中使用 Tiptap 进行 SSR

Tiptap 可以与 React 应用中的服务端渲染（SSR）一起使用。然而，为确保编辑器仅在客户端初始化，您需要在创建编辑器实例时使用 `immediatelyRender` 选项，防止它在服务器端渲染。

以下是一个设置 React 组件中 Tiptap SSR 的示例：

```jsx
'use client'

import { useEditor, EditorContent } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'

export function MyEditor() {
  const editor = useEditor({
    extensions: [StarterKit],
    content: '<p>Hello World!</p>',
    // 禁用即时渲染以避免 SSR 问题
    immediatelyRender: false,
  })

  if (!editor) {
    return null // 确保编辑器初始化前不渲染
  }

  return <EditorContent editor={editor} />
}
```

`Tiptap.Loading` 组件在 SSR 中非常有用，它会在编辑器于客户端初始化前显示占位内容。

## 优化您的性能

我们建议您访问[React 性能指南](https://tiptap.zhcndoc.com/guides/performance.md)，以高效集成 Tiptap 编辑器。这将帮助您避免应用规模扩大时可能出现的问题。

## 另一种方案：使用 EditorContent 手动设置

对于需要更控制力的场景，可以直接使用 `EditorContent`：

```tsx
// src/Editor.tsx
import { useEditor, EditorContent } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'

function Editor() {
  const editor = useEditor({
    extensions: [StarterKit], // 定义您的扩展数组
    content: '<p>Hello World!</p>', // 初始内容
  })

  return <EditorContent editor={editor} />
}

export default Editor
```

此方法要求手动为每个组件传入 `editor` 属性。大多数情况下推荐使用 `<Tiptap>` 组件，它减少了模板代码并自动提供上下文。

## API 参考

### Tiptap 组件

根提供者组件，通过 React 上下文使编辑器实例可用。

| 参数         | 类型               | 描述                      |
| ---------- | ---------------- | ----------------------- |
| `instance` | `Editor \| null` | 来自 `useEditor()` 的编辑器实例 |
| `children` | `ReactNode`      | 子组件                     |

### useTiptap 钩子

返回 Tiptap 的上下文值。

```tsx
const { editor, isReady } = useTiptap()
```

| 属性        | 类型               | 描述         |
| --------- | ---------------- | ---------- |
| `editor`  | `Editor \| null` | 编辑器实例      |
| `isReady` | `boolean`        | 编辑器是否完成初始化 |

### useTiptapState 钩子

使用选择器函数订阅编辑器状态的一部分。

```tsx
const value = useTiptapState(selector, equalityFn?)
```

| 参数           | 类型                                  | 描述             |
| ------------ | ----------------------------------- | -------------- |
| `selector`   | `(state: EditorStateSnapshot) => T` | 状态选择函数         |
| `equalityFn` | `(a: T, b: T) => boolean`           | 可选的比较函数，控制重新渲染 |

## 接下来

- [配置您的编辑器](https://tiptap.zhcndoc.com/editor/getting-started/configure.md)
- [为您的编辑器添加样式](https://tiptap.zhcndoc.com/editor/getting-started/style-editor.md)
- [了解更多 Tiptap 的概念](https://tiptap.zhcndoc.com/editor/core-concepts/introduction.md)
- [了解如何持久化编辑器状态](https://tiptap.zhcndoc.com/editor/core-concepts/persistence.md)
- [开始构建您自己的扩展](https://tiptap.zhcndoc.com/editor/extensions/custom-extensions.md)
