Svelte bindings for Pillar — Cursor for your product.
Pillar is an embedded AI co-pilot that helps users complete tasks, not just answer questions. Users say what they want, and Pillar uses your UI to make it happen — navigating pages, pre-filling forms, and calling your APIs.
usePillar and useHelpPanel reactive storesView Full Documentation | Svelte Guide | API Reference
npm install @pillar-ai/svelte
# or
pnpm add @pillar-ai/svelte
# or
yarn add @pillar-ai/svelte
First, register your product in the Pillar app:
Wrap your app with PillarProvider and define actions:
<!-- +layout.svelte -->
<script lang="ts">
import { PillarProvider } from '@pillar-ai/svelte';
import { goto } from '$app/navigation';
const actions = {
export_to_csv: {
type: 'trigger' as const,
label: 'Export to CSV',
description: 'Export current data to CSV file',
},
go_to_settings: {
type: 'navigate' as const,
label: 'Open Settings',
description: 'Navigate to settings page',
},
};
function handleTask(task: { name: string; data: any }) {
if (task.name === 'go_to_settings') {
goto('/settings');
}
if (task.name === 'export_to_csv') {
downloadCSV();
}
}
</script>
<PillarProvider
productKey="your-product-key"
{actions}
onTask={handleTask}
>
<slot />
</PillarProvider>
Actions define what your co-pilot can do. When users make requests, Pillar matches intent to actions:
const actions = {
// Navigation actions
go_to_billing: {
type: 'navigate' as const,
label: 'Open Billing',
description: 'Navigate to billing and subscription settings',
},
// Trigger actions that execute code
export_report: {
type: 'trigger' as const,
label: 'Export Report',
description: 'Export the current report to PDF or CSV',
},
// Actions with data schemas (AI extracts parameters)
invite_team_member: {
type: 'trigger' as const,
label: 'Invite Team Member',
description: 'Send an invitation to join the team',
dataSchema: {
email: { type: 'string' as const, required: true },
role: { type: 'string' as const, enum: ['admin', 'member', 'viewer'] },
},
},
};
Access the full SDK instance and state:
<script lang="ts">
import { usePillar } from '@pillar-ai/svelte';
const { isReady, isPanelOpen, open, close, toggle } = usePillar();
</script>
{#if $isReady}
<button on:click={toggle}>
{$isPanelOpen ? 'Close Co-pilot' : 'Open Co-pilot'}
</button>
{/if}
Returns:
| Property | Type | Description |
|---|---|---|
pillar |
Writable | The SDK instance |
isReady |
Readable | SDK initialization state |
isPanelOpen |
Writable | Panel open state |
open(options?) |
Function | Open the panel |
close() |
Function | Close the panel |
toggle() |
Function | Toggle the panel |
openArticle(slug) |
Function | Open a specific article |
setTheme(theme) |
Function | Update theme at runtime |
Simplified panel controls:
<script lang="ts">
import { useHelpPanel } from '@pillar-ai/svelte';
const { isOpen, toggle, openChat } = useHelpPanel();
</script>
<button on:click={toggle}>{$isOpen ? 'Close' : 'Ask Co-pilot'}</button>
<button on:click={openChat}>Start Chat</button>
The main provider component:
<PillarProvider
productKey="your-product-key"
actions={actions}
onTask={(task) => handleTask(task)}
config={{
panel: { position: 'right' },
edgeTrigger: { enabled: true },
}}
>
<slot />
</PillarProvider>
For custom panel placement:
<PillarProvider
productKey="your-product-key"
config={{ panel: { container: 'manual' } }}
>
<div class="layout">
<PillarPanel class="sidebar-panel" />
<main>Your content</main>
</div>
</PillarProvider>
Render custom UI for inline actions:
<!-- InviteCard.svelte -->
<script lang="ts">
import type { CardComponentProps } from '@pillar-ai/svelte';
let { data, onConfirm, onCancel, onStateChange }: CardComponentProps<{
email: string;
role: string;
}> = $props();
async function handleConfirm() {
onStateChange?.('loading', 'Sending invite...');
try {
await sendInvite(data.email, data.role);
onStateChange?.('success', 'Invite sent!');
onConfirm();
} catch (e) {
onStateChange?.('error', 'Failed to send invite');
}
}
</script>
<div class="card">
<p>Invite {data.email} as {data.role}?</p>
<button on:click={handleConfirm}>Send Invite</button>
<button on:click={onCancel}>Cancel</button>
</div>
Register in the provider:
<script lang="ts">
import { PillarProvider } from '@pillar-ai/svelte';
import InviteCard from './InviteCard.svelte';
</script>
<PillarProvider
productKey="your-product-key"
cards={{ invite_team_member: InviteCard }}
>
<slot />
</PillarProvider>
For SvelteKit apps, add the provider in your root layout:
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import { PillarProvider } from '@pillar-ai/svelte';
import { goto } from '$app/navigation';
let { children } = $props();
const actions = {
navigate: {
type: 'navigate' as const,
label: 'Navigate',
description: 'Navigate to a page in the app',
},
};
</script>
<PillarProvider
productKey="your-product-key"
{actions}
onTask={(task) => {
if (task.name === 'navigate') {
goto(task.data.path);
}
}}
>
{@render children()}
</PillarProvider>
| Package | Description |
|---|---|
| @pillar-ai/sdk | Core vanilla JavaScript SDK |
| @pillar-ai/react | React bindings |
| @pillar-ai/vue | Vue 3 bindings |
MIT