Skip to content

pillarhq/sdk-react

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

48 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@pillar-ai/react

React bindings for Pillar, the open-source product copilot SDK. Add an AI assistant to your React or Next.js app that executes tasks using your UI. GitHub · Docs

npm version npm downloads License: MIT TypeScript

What is Pillar?

Pillar is a product copilot for SaaS and web apps. Users say what they want, and Pillar uses your UI to make it happen.

A CRM user could ask:

"Close the Walmart deal as won and notify implementation"

An analytics user could ask:

"Add a weekly signups chart to my dashboard"

Pillar understands the intent, builds a plan, and executes it client-side with the user's session. No proxy servers, no token forwarding.

Documentation

Full docs · React quickstart · API reference

Installation

npm install @pillar-ai/react
# or
pnpm add @pillar-ai/react
# or
yarn add @pillar-ai/react

Quick start

1. Get your agent slug

Sign up at app.trypillar.com and grab your agent slug from the dashboard.

2. Add the provider

Wrap your app with PillarProvider:

import { PillarProvider } from "@pillar-ai/react";

function App() {
  return (
    <PillarProvider agentSlug="your-agent-slug">
      <YourApp />
    </PillarProvider>
  );
}

For Next.js App Router, create a client wrapper:

// providers/PillarSDKProvider.tsx
"use client";

import { PillarProvider } from "@pillar-ai/react";

export function PillarSDKProvider({ children }: { children: React.ReactNode }) {
  return (
    <PillarProvider agentSlug="your-agent-slug">{children}</PillarProvider>
  );
}
// app/layout.tsx
import { PillarSDKProvider } from "@/providers/PillarSDKProvider";

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <PillarSDKProvider>{children}</PillarSDKProvider>
      </body>
    </html>
  );
}

3. Define your first tool

Tools tell Pillar what your app can do. Define them with the usePillarTool hook:

import { usePillarTool } from "@pillar-ai/react";
import { useRouter } from "next/navigation";

export function usePillarTools() {
  const router = useRouter();

  usePillarTool({
    name: "open_settings",
    type: "navigate",
    description: "Navigate to the settings page",
    examples: ["open settings", "go to settings"],
    autoRun: true,
    execute: () => router.push("/settings"),
  });
}

Call usePillarTools() from any component inside PillarProvider (e.g. your layout).

Defining tools

Tools are the building blocks. When a user makes a request, Pillar matches intent to tools and executes them.

Single tool

usePillarTool({
  name: "open_dashboard",
  type: "navigate",
  description: "Navigate to the main dashboard",
  examples: ["go to dashboard", "show me the dashboard"],
  execute: () => router.push("/dashboard"),
});

Tool with input schema

Pillar extracts parameters from the user's request:

usePillarTool({
  name: "view_user_profile",
  type: "navigate",
  description: "View a specific user's profile page",
  inputSchema: {
    type: "object",
    properties: {
      userId: { type: "string", description: "The user ID to view" },
    },
    required: ["userId"],
  },
  execute: ({ userId }) => router.push(`/users/${userId}`),
});

Multiple tools at once

usePillarTool([
  {
    name: "open_billing",
    type: "navigate",
    description: "Navigate to billing and subscription settings",
    examples: ["go to billing", "view my subscription"],
    execute: () => router.push("/settings/billing"),
  },
  {
    name: "open_team",
    type: "navigate",
    description: "Navigate to team management page",
    examples: ["manage team", "invite team members"],
    execute: () => router.push("/settings/team"),
  },
]);

Tool types

Type Use case
navigate Navigate to a page or view
trigger_tool Run a function (export, toggle, API call)
query Return data to the assistant
inline_ui Render a custom component in the chat
external_link Open an external URL
copy_text Copy text to clipboard

See Setting Up Tools for the full guide.

Hooks

usePillar

Access SDK state and methods:

import { usePillar } from "@pillar-ai/react";

function MyComponent() {
  const {
    pillar, // SDK instance
    isReady, // Whether SDK is initialized
    isPanelOpen, // Panel open state
    open, // Open the panel
    close, // Close the panel
    toggle, // Toggle the panel
    navigate, // Navigate to a view
    setTheme, // Update theme at runtime
    on, // Subscribe to events
  } = usePillar();

  if (!isReady) return <div>Loading...</div>;

  return <button onClick={() => open()}>Get Help</button>;
}

useHelpPanel

Panel-specific controls:

import { useHelpPanel } from "@pillar-ai/react";

function HelpButton() {
  const {
    isOpen,
    open,
    close,
    toggle,
    openArticle,
    openCategory,
    openSearch,
    openChat,
  } = useHelpPanel();

  return (
    <div>
      <button onClick={toggle}>{isOpen ? "Close" : "Help"}</button>
      <button onClick={openChat}>Ask co-pilot</button>
    </div>
  );
}
Method Description
open(options?) Open the panel
close() Close the panel
toggle() Toggle open/closed
openArticle(slug) Open a specific article
openCategory(slug) Open a category view
openSearch(query?) Open search with optional query
openChat() Open the co-pilot chat view

Components

PillarProvider

The root component. Initializes and configures the SDK.

Prop Type Required Description
agentSlug string Yes Your agent slug from app.trypillar.com
config object No SDK configuration (panel, theme, triggers)
onTask (task) => void No Generic handler for dynamic tools
<PillarProvider
  agentSlug="your-agent-slug"
  config={{
    // userApiToken: accessToken, // forward auth to API tool sources
    panel: { position: "right", mode: "push", width: 400 },
    theme: { mode: "auto", colors: { primary: "#2563eb" } },
  }}
>
  {children}
</PillarProvider>

PillarPanel

For custom panel placement, set panel.container to 'manual' and render PillarPanel where you want it:

import { PillarProvider, PillarPanel } from "@pillar-ai/react";

function App() {
  return (
    <PillarProvider
      agentSlug="your-agent-slug"
      config={{ panel: { container: "manual" } }}
    >
      <div className="layout">
        <main>Your content</main>
        <PillarPanel className="sidebar-panel" />
      </div>
    </PillarProvider>
  );
}

Generic task handler

For dynamic or backend-triggered tools, use the onTask prop on PillarProvider. For most cases, prefer usePillarTool which co-locates the handler with the tool definition.

<PillarProvider
  agentSlug="your-agent-slug"
  onTask={(task) => {
    if (task.name.startsWith("nav_")) {
      router.push(task.data.path);
      return;
    }

    switch (task.name) {
      case "notification":
        showNotification(task.data.message);
        break;
      case "refresh_data":
        queryClient.invalidateQueries();
        break;
    }
  }}
>
  {children}
</PillarProvider>

Inline UI with render

For inline_ui tools, use the render prop to display custom React components in the chat. The AI agent provides data directly to the render component — no execute function needed:

import { usePillarTool, type ToolRenderProps } from "@pillar-ai/react";

function InviteCard({ data }: ToolRenderProps<{ emails: string[]; teamName: string }>) {
  return (
    <div className="p-4 border rounded">
      <p>Invite {data.emails?.length || 0} members to {data.teamName}</p>
    </div>
  );
}

usePillarTool({
  name: "invite_members",
  description: "Invite team members via email",
  type: "inline_ui",
  inputSchema: {
    type: "object",
    properties: {
      emails: { type: "array", items: { type: "string" } },
      teamName: { type: "string" },
    },
  },
  render: InviteCard,
});

The render component receives:

  • data — Data provided by the AI agent (extracted from the conversation via inputSchema)
  • sendResult(result) — Send a result back to the AI agent, continuing the conversation
  • context — Card position info (isLatest, isReady, messageIndex, segmentIndex, toolName)
  • onStateChange?(state, message?) — optional loading/success/error states

Confirmation UI

For tools that perform destructive or irreversible actions, use needsConfirmation to require user approval before execute runs:

usePillarTool({
  name: "delete_project",
  description: "Permanently delete a project and all its data",
  type: "trigger_tool",
  needsConfirmation: true,
  inputSchema: {
    type: "object",
    properties: {
      projectId: { type: "string", description: "Project ID to delete" },
    },
    required: ["projectId"],
  },
  execute: async ({ projectId }) => {
    await api.deleteProject(projectId);
    return { deleted: true };
  },
});

The SDK shows a Confirm/Cancel card. The user must click Confirm before execute runs.

For custom confirmation UI, use renderConfirmation:

import { usePillarTool, type ConfirmationRenderProps } from "@pillar-ai/react";

function ConfirmPurchase({
  data,
  onConfirm,
  onCancel,
  isReady,
  isLatest,
}: ConfirmationRenderProps<{ total: number; cartId: string }>) {
  return (
    <div className={`p-4 border rounded ${!isLatest ? "opacity-50" : ""}`}>
      <p>Complete purchase for ${data.total}?</p>
      <div className="flex gap-2 mt-4">
        <button onClick={() => onConfirm()} disabled={!isReady}>
          Buy Now
        </button>
        <button onClick={onCancel}>Cancel</button>
      </div>
    </div>
  );
}

usePillarTool({
  name: "complete_purchase",
  description: "Complete the purchase",
  type: "trigger_tool",
  renderConfirmation: ConfirmPurchase,
  execute: async ({ cartId }) => {
    await api.checkout(cartId);
    return { success: true };
  },
});

Providing renderConfirmation implies needsConfirmation — you don't need to set both.

The ConfirmationRenderProps interface provides:

Prop Type Description
data T Data the AI extracted via inputSchema
onConfirm (modifiedData?) => void Approve the action. Optionally pass modified data to override what the AI sent to execute.
onCancel () => void Dismiss the confirmation without executing
isReady boolean True when the AI is not streaming — safe to interact
isLatest boolean True when this is the most recent card in the chat

Exports

Export Description
PillarProvider Context provider that initializes the SDK
PillarPanel Component for custom panel placement
usePillar Hook for SDK access and panel control
useHelpPanel Hook for panel-specific controls
usePillarTool Hook to register tools

Related packages

Package Description
@pillar-ai/sdk Core vanilla JS SDK
@pillar-ai/vue Vue 3 bindings
@pillar-ai/svelte Svelte bindings

Requirements

  • React 17+ or React 19+
  • React DOM 17+ or React DOM 19+

License

MIT

Releases

Packages

Contributors

Languages