---
title: "Insert content workflow"
description: "Use the Tiptap AI Toolkit to build a workflow that inserts or replaces content in your documents."
canonical_url: "https://tiptap.dev/docs/content-ai/capabilities/ai-toolkit/workflows/insert-content"
---

# Insert content workflow

Use the Tiptap AI Toolkit to build a workflow that inserts or replaces content in your documents.

Build a workflow that inserts or replaces content in your Tiptap documents.

> **Interactive demo:** [insert content workflow](https://ai-toolkit-demos.vercel.app/insert-content-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/) + [OpenAI](https://openai.com/) models
- Tiptap AI Toolkit

## Project overview

This demo uses the AI Toolkit's Insert content workflow to replace selected content with AI-generated content in real-time.

## Installation

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

```bash
npx create-next-app@latest insert-content-workflow
```

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

```bash
npm install @tiptap/react @tiptap/starter-kit ai @ai-sdk/react @ai-sdk/openai
```

Install the Tiptap AI Toolkit:

> **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
```

## Server setup

Create an API endpoint that uses the [Vercel AI SDK](https://ai-sdk.dev/) to call the OpenAI 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 insert content workflow, using the `createInsertContentWorkflow` function. The workflow includes a ready-to-use system prompt that instructs the AI model on how to generate the content.

The user message should be a JSON object with the following properties:

- `task`: the task to complete
- `replace`: the HTML content to be replaced (optional)
- `before`: the HTML content before (optional)
- `after`: the HTML content after (optional)

As the AI model generates its response, the API endpoint streams the HTML content to the client.

```ts
// app/api/insert-content-workflow/route.ts
import { openai } from '@ai-sdk/openai'
import { createInsertContentWorkflow } from '@tiptap-pro/ai-toolkit-tool-definitions'
import { streamText } from 'ai'

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

  // Create and configure the insert content workflow (with the default settings).
  // It includes the ready-to-use system prompt.
  const workflow = createInsertContentWorkflow()

  const result = streamText({
    model: openai('gpt-5.4-mini'),
    // System prompt
    system: workflow.systemPrompt,
    // User message with the task and the content to replace, as a JSON object.
    prompt: JSON.stringify({
      task,
      replace,
    }),
  })

  return result.toTextStreamResponse()
}
```

## Client setup

Create a React component that renders the editor and streams the AI-generated content into the selection.

First, when the workflow starts, get the current selection with the `getHtmlRange` method of the AI Toolkit. Then, call the API endpoint to start the workflow. The API endpoint will return a stream of HTML that can be inserted into the editor with the `streamHtml` method of the AI Toolkit.

```tsx
// app/insert-content-workflow/page.tsx
'use client'

import { EditorContent, useEditor, useEditorState } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import { AiToolkit, getAiToolkit } from '@tiptap-pro/ai-toolkit'
import { useState } from 'react'
import { Selection } from '@tiptap/extensions'

export default function Page() {
  const editor = useEditor({
    immediatelyRender: false,
    extensions: [StarterKit, AiToolkit, Selection],
    content: `<p>Select some text and click the "Add emojis" button to add emojis to your selection.</p>`,
  })

  // Show a loading state when the AI is generating content
  const [isLoading, setIsLoading] = useState(false)

  // Disable the buttons when the selection is empty
  const selectionIsEmpty = useEditorState({
    editor,
    selector: (snapshot) => snapshot.editor?.state.selection.empty ?? true,
  })

  if (!editor) return null

  const editSelection = async (task: string) => {
    editor.commands.blur()
    setIsLoading(true)

    const toolkit = getAiToolkit(editor)

    // Use the AI Toolkit to get the selection in HTML format
    const selection = toolkit.getHtmlRange(editor.state.selection)
    const selectionPosition = editor.state.selection

    // Call the API endpoint to get the edited HTML content
    const response = await fetch('/api/insert-content-workflow', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        task,
        replace: selection,
      }),
    })

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`)
    }

    // The response is a stream of HTML content
    const readableStream = response.body
    if (!readableStream) {
      throw new Error('No response body')
    }

    // Use the AI Toolkit to stream HTML into the selection
    await toolkit.streamHtml(readableStream, {
      position: selectionPosition,
      // Update the selection during streaming so that the selection always
      // spans the generated content
      onChunkInserted(event) {
        editor.commands.setTextSelection(event.range)
      },
    })

    setIsLoading(false)
  }

  const disabled = selectionIsEmpty || isLoading

  return (
    <div>
      <EditorContent editor={editor} />
      <button onClick={() => editSelection('Add emojis to this text')} disabled={disabled}>
        {isLoading ? 'Loading...' : 'Add emojis'}
      </button>
    </div>
  )
}
```

## End result

With additional CSS styles, the result is a polished application that can insert or replace content with AI-generated content in real-time:

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

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

## Show the AI Caret

You can add the `AiCaret` extension to display a cursor that indicates where the AI is inserting content. This gives users real-time visual feedback during streaming.

```tsx
import { AiCaret, AiToolkit, getAiToolkit } from '@tiptap-pro/ai-toolkit'

const editor = useEditor({
  extensions: [StarterKit, AiToolkit, AiCaret],
})
```

See the [AI Caret guide](https://tiptap.dev/docs/content-ai/capabilities/ai-toolkit/advanced-guides/ai-caret.md) for configuration options and CSS styles.

## Disable streaming

To insert content into the document without streaming it, use the `insertHtml` method. This method has the same options as `streamHtml`, only that it is called once with the full HTML content as argument. [See the API reference](https://tiptap.dev/docs/content-ai/capabilities/ai-toolkit/api-reference/edit-the-document.md#inserthtml).

## Show a review UI

Display changes in a review UI, so users can accept or reject them.

There are two approaches:

- Use the [**Tracked Changes**](https://tiptap.dev/docs/tracked-changes/getting-started/overview.md) extension to render the review UI. Changes persist as part of the document and are visible to other users.
- **AI Toolkit suggestions**: a decoration-based UI that's ephemeral and only visible to the current user of the document.

Configure the review UI by setting the `reviewOptions` parameter. Changes will show up as suggestions that users can accept or reject.

```tsx
await toolkit.streamHtml(readableStream, {
  // ... other options

  reviewOptions: {
    // Show the review UI with Tracked Changes
    mode: 'trackedChanges',

    // Preview changes before inserting them into the document,
    // using AI Toolkit suggestions
    mode: 'preview',

    // Show a review UI with AI Toolkit suggestions
    mode: 'review',
  },
})
```

See the [review changes](https://tiptap.dev/docs/content-ai/capabilities/ai-toolkit/agents/review-changes.md) guide to learn more about the review UI.

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

See the [AI Toolkit demos](https://github.com/ueberdosis/ai-toolkit-demos) for examples on how to use AI Toolkit workflows with the review UI.

## Next steps

- [API reference](https://tiptap.dev/docs/content-ai/capabilities/ai-toolkit/api-reference/workflows.md#createinsertcontentworkflow) of the `createInsertContentWorkflow` function
- [API reference](https://tiptap.dev/docs/content-ai/capabilities/ai-toolkit/api-reference/read-the-document.md#gethtmlrange) of the `getHtmlRange` method
- [API reference](https://tiptap.dev/docs/content-ai/capabilities/ai-toolkit/api-reference/edit-the-document.md#streamhtml) of the `streamHtml` method
- [Suggestions](https://tiptap.dev/docs/content-ai/capabilities/ai-toolkit/agents/review-changes.md): Learn about reviewing the document and displaying suggestions in the editor.
- [AI Caret](https://tiptap.dev/docs/content-ai/capabilities/ai-toolkit/advanced-guides/ai-caret.md): Show a cursor where the AI is inserting content.
