---
title: "Comments workflow"
description: "Use the Tiptap AI Toolkit to build a workflow that manages comments and threads in your documents."
canonical_url: "https://tiptap.dev/docs/content-ai/capabilities/ai-toolkit/workflows/comments"
---

# Comments workflow

Use the Tiptap AI Toolkit to build a workflow that manages comments and threads in your documents.

Build a workflow that allows the AI to manage comments and threads in your Tiptap documents.

> **Interactive demo:** [comments workflow](https://ai-toolkit-demos.vercel.app/comments-workflow)

See the [source code on GitHub](https://github.com/ueberdosis/ai-toolkit-demos).

## Tech stack

- [React](https://react.dev/) + [Next.js](https://nextjs.org/)
- [AI SDK by Vercel](https://ai-sdk.dev/) + [Anthropic](https://www.anthropic.com/) models
- Tiptap AI Toolkit
- Tiptap Comments extension

## Project overview

This demo uses the AI Toolkit's Comments workflow to manage threads and comments in the document. The AI can create new threads, add comments, update comments, remove comments, and manage thread status.

## Installation

Create a [Next.js](https://nextjs.org/) project:

```bash
npx create-next-app@latest comments-workflow
```

Install the core Tiptap packages and the [Vercel AI SDK](https://ai-sdk.dev/) for Anthropic:

```bash
npm install @tiptap/react @tiptap/starter-kit ai @ai-sdk/react @ai-sdk/anthropic zod uuid
```

Install the Tiptap AI Toolkit and Comments extension:

> **Pro package:**
>
> The AI Toolkit is a pro package. Before installation, set up access to the private NPM registry by
> following the [private registry guide](https://tiptap.dev/docs/guides/pro-extensions.md).

```bash
npm install @tiptap-pro/ai-toolkit @tiptap-pro/ai-toolkit-tool-definitions @tiptap-pro/extension-comments
```

## Server setup

Create an API endpoint that uses the [Vercel AI SDK](https://ai-sdk.dev/) to call the Anthropic model.

If your backend is in another programming language than TypeScript, see [this guide](https://tiptap.dev/docs/content-ai/capabilities/ai-toolkit/advanced-guides/non-typescript-backends.md).

Inside the API endpoint, create and configure the Comments workflow using the `createEditThreadsWorkflow` function. The workflow includes a ready-to-use system prompt that instructs the AI model on how to manage comments.

The user message should include:

- `content`: The content of the document (obtained from `tiptapRead`)
- `threads`: The existing threads in the document (obtained from `getThreads`)
- `task`: The task to be performed by the AI. For example, `Add a comment suggesting improvements to the introduction`.

```ts
// app/api/comments-workflow/route.ts
import { openai } from '@ai-sdk/openai'
import { createEditThreadsWorkflow } from '@tiptap-pro/ai-toolkit-tool-definitions'
import { Output, streamText } from 'ai'

export async function POST(req: Request) {
  const { content, threads, task } = await req.json()

  // Create and configure the Comments workflow (with the default settings).
  // It includes the ready-to-use system prompt and the output schema.
  const workflow = createEditThreadsWorkflow()

  const result = streamText({
    model: openai('gpt-5.4-mini'),
    // System prompt
    system: workflow.systemPrompt,
    // User message
    prompt: JSON.stringify({
      content,
      threads,
      task,
    }),
    output: Output.object({ schema: workflow.zodOutputSchema }),
  })

  return result.toTextStreamResponse()
}
```

## Client setup

Create a React component that renders the editor with the Comments extension and applies comment operations.

The workflow supports streaming, which means operations are applied incrementally as they arrive from the AI model, providing real-time feedback to users.

First, when the workflow starts, call the `tiptapRead` method to get the document content and `getThreads` method to get existing threads.

Then, call the API endpoint to start the workflow. Use the `editThreadsWorkflow` method to apply the comment operations. The `isStreaming` parameter ensures operations are applied incrementally as they stream in.

```tsx
// app/comments-workflow/page.tsx
'use client'

import { experimental_useObject as useObject } from '@ai-sdk/react'
import { EditorContent, useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import { AiToolkit, getAiToolkit, editThreadsWorkflowOutputSchema } from '@tiptap-pro/ai-toolkit'
import { Comments } from '@tiptap-pro/extension-comments'
import { useEffect, useState } from 'react'
import { v4 as uuid } from 'uuid'

// Create a simple comments provider
const commentsProvider = {
  threads: [],
  getThreads: () => commentsProvider.threads,
  createThread: (thread) => {
    commentsProvider.threads.push(thread)
    return thread
  },
  updateThread: (id, data) => {
    const index = commentsProvider.threads.findIndex((t) => t.id === id)
    if (index !== -1) {
      commentsProvider.threads[index] = { ...commentsProvider.threads[index], ...data }
    }
  },
  deleteThread: (id) => {
    commentsProvider.threads = commentsProvider.threads.filter((t) => t.id !== id)
  },
}

export default function Page() {
  const editor = useEditor({
    immediatelyRender: false,
    extensions: [
      StarterKit,
      AiToolkit,
      Comments.configure({
        provider: commentsProvider,
      }),
    ],
    content: `<h1>Document with Comments</h1><p>This is a sample document where AI can add and manage comments.</p>`,
  })

  const [workflowId, setWorkflowId] = useState('')
  const [task, setTask] = useState('Add a comment suggesting improvements to this document')

  const { submit, isLoading, object } = useObject({
    api: '/api/comments-workflow',
    schema: editThreadsWorkflowOutputSchema,
  })

  // Apply operations as they stream in
  useEffect(() => {
    if (!editor || !object?.operations) return

    const toolkit = getAiToolkit(editor)
    toolkit.editThreadsWorkflow({
      operations: object.operations,
      workflowId,
      isStreaming: isLoading,
    })
  }, [editor, object, workflowId, isLoading])

  if (!editor) return null

  const manageComments = () => {
    setWorkflowId(uuid())

    const toolkit = getAiToolkit(editor)

    // Get the document content and existing threads
    const { content } = toolkit.tiptapRead()
    const { threads } = toolkit.getThreads()

    // Call the API endpoint to start the workflow
    submit({ content, threads, task })
  }

  return (
    <div>
      <EditorContent editor={editor} />

      <input
        type="text"
        value={task}
        onChange={(e) => setTask(e.target.value)}
        placeholder="Enter task for comments..."
      />

      <button onClick={manageComments} disabled={isLoading}>
        {isLoading ? 'Processing...' : 'Manage Comments with AI'}
      </button>
    </div>
  )
}
```

## Available operations

The Comments workflow supports the following operations:

| Operation        | Description                                 |
| ---------------- | ------------------------------------------- |
| Create thread    | Creates a new thread at a specific location |
| Create comment   | Adds a comment to an existing thread        |
| Update comment   | Updates an existing comment                 |
| Remove comment   | Removes a comment from a thread             |
| Remove thread    | Removes an entire thread                    |
| Resolve thread   | Marks a thread as resolved                  |
| Unresolve thread | Marks a thread as unresolved                |

## End result

With additional CSS styles, the result is a polished comments management application:

> **Interactive demo:** [comments workflow](https://ai-toolkit-demos.vercel.app/comments-workflow)

See the [source code on GitHub](https://github.com/ueberdosis/ai-toolkit-demos).

## Comment on part of the document

To add comments to only a sub-section of the document, set the `range` option on `tiptapRead`. This argument takes a [Range](https://tiptap.dev/docs/content-ai/capabilities/ai-toolkit/advanced-guides/concepts.md#range) with the region to read.

For example, to comment on only the selected content:

```ts
const range = editor.state.selection

// Read only the selected part of the document
const { content } = toolkit.tiptapRead({ range })
const { threads } = toolkit.getThreads()

const operations = callApi(content, threads)

// Apply comment operations
toolkit.editThreadsWorkflow({ operations, workflowId })
```

For large documents, you can use the [`tiptapReadChunks`](https://tiptap.dev/docs/content-ai/capabilities/ai-toolkit/api-reference/read-the-document.md#tiptapreadchunks) method to split the document into chunks and process them in parallel. Each chunk includes a [Range](https://tiptap.dev/docs/content-ai/capabilities/ai-toolkit/advanced-guides/concepts.md#range) with the region of that chunk.

## Related guides

- [API reference](https://tiptap.dev/docs/content-ai/capabilities/ai-toolkit/api-reference/read-the-document.md#tiptapread) of the `tiptapRead` method
- [API reference](https://tiptap.dev/docs/content-ai/capabilities/ai-toolkit/api-reference/workflows.md#getthreads) of the `getThreads` method
- [API reference](https://tiptap.dev/docs/content-ai/capabilities/ai-toolkit/api-reference/workflows.md#editthreadsworkflow) of the `editThreadsWorkflow` method
- [Comments extension](https://tiptap.dev/docs/comments/getting-started/overview.md): Learn more about the Comments extension.
