Generate experiences with Puck AI
Read docs
Puck AICustom Agents

Custom Agents

Build custom chat and AI streaming interfaces using the usePuckAi() API.

Build a chat interface

Use usePuckAi().chat to hold a conversation with the agent, sending and reading messages. Changes are automatically applied to the canvas.

import { useState } from "react";
import { Puck } from "@puckeditor/core";
import { usePuckAi } from "@puckeditor/plugin-ai";
 
function ChatPlugin() {
  const { chat } = usePuckAi();
  const [input, setInput] = useState("");
 
  return (
    <div>
      {chat.messages.map((message) =>
        message.parts.map((part, i) =>
          part.type === "text" ? <p key={i}>{part.text}</p> : null
        )
      )}
 
      <form
        onSubmit={(e) => {
          e.preventDefault();
          chat.sendMessage(input);
          setInput("");
        }}
      >
        <input value={input} onChange={(e) => setInput(e.target.value)} />
        <button>Send</button>
      </form>
    </div>
  );
}
 
export function Editor() {
  return (
    <Puck
      config={config}
      data={data}
      plugins={[{ name: "chat", label: "Chat", render: ChatPlugin }]}
    />
  );
}

Show the agent’s progress

Look up each tool part in chat.toolStatus to show what the agent is doing.

message.parts.map((part, i) => {
  if (part.type === "text") return <p key={i}>{part.text}</p>;
 
  // For example, "Updating page"
  if ("toolCallId" in part) {
    return <p key={i}>{chat.toolStatus[part.toolCallId]?.label}</p>;
  }
 
  return null;
});

Request a single change

Use usePuckAi().generate() to request a change to the current page, outside of a conversation.

import { usePuckAi } from "@puckeditor/plugin-ai";
 
function TranslatePlugin({ language }) {
  const { generate } = usePuckAi();
 
  return (
    <button onClick={() => generate(`Translate this page to ${language}`)}>
      Translate
    </button>
  );
}

Replaying generations

You can replay a generation created headlessly using the server generate() API.

To record a generation, use onPart to store each AI Part:

import { generate } from "@puckeditor/cloud-client";
 
await generate({
  prompt: "Add a pricing section",
  config,
  pageData,
  onPart: (part) => {
    // Store the part
  },
});

To replay the parts into the editor, provide the stored parts to usePuckAi().applyPart:

import { usePuckAi } from "@puckeditor/plugin-ai";
 
function ApplyPlugin() {
  const { applyPart } = usePuckAi();
 
  const apply = async () => {
    const parts = getMyParts(); // Get parts from wherever you stored them
 
    for await (const part of parts) {
      await applyPart(part); // Apply them
    }
  };
 
  return <button onClick={apply}>Apply</button>;
}

By default, the server’s generate will run a single iteration. See multi-step headless generation to learn more.

Further reading