Techniques
Actions
A key capability of Copilot.js is to perform actions in your frontend app, as directed by the user. For example, the user can ask:
- "Insert an image that matches the text."
- "Rename all files to lowercase."
- "Translate the selection to Spanish."
The principle
Consider the example of a drawing app, which has CRUD functions for manipulating shapes
, such as createShape
, updateShape
, and so on. These functions are typically implemented inside a frontend store, using Redux or Zustand.
To perform a user action, the copilot invokes the appropriate functions in your app's store:
Enabling actions
-
Write type definitions for the actions that you'll be exposing to Copilot. In our drawing app example, let's create a separate file:
// actionTypes.ts export const actionTypes = ` // -------------------- Actions -------------------- type createShape = (shape: Shape) => ShapeWithId type updateShape = (shapeId: string, updatedShape: Partial<Shape>) => void type deleteShape = (shapeId: string) => void type getShape = (shapeId: string) => ShapeWithId type listShapes = (filters: Partial<ShapeWithId>) => ShapeWithId[] // -------------------- Shapes -------------------- type Shape = Square | Circle type Circle = { type: 'circle'; x: number; y: number; radius: number } type Square = { type: 'square'; x: number; y: number; length: number } // Extend shapes with IDs type ShapeWithId = SquareWithId | CircleWithId type CircleWithId = Circle & { id: string } type SquareWithId = Square & { id: string } `
There might be a simpler way to import your action types, depending on your bundler.
-
Supply the
actionTypes
andactions
to the<CopilotProvider>
's context:// App.jsx import { actionTypes } from './actionTypes' import store from './store' // Your application's store. import { CopilotProvider } from '@copilotjs/react' import { Copilot } from '@/components/Copilot' export default function App() { return ( <CopilotProvider context={{ // A string with type definitions of exposed actions. actionTypes: actionTypes, // A map from each action's name to its function. actions: { createShape: store.createShape, updateShape: store.updateShape, deleteShape: store.deleteShape, getShape: store.getShape, listShapes: store.listShapes, }, }} // ... > <Copilot className="h-[600px] w-[400px]" /> </CopilotProvider> ) }
Now, <CopilotProvider>
can invoke actions in your app's store:
Importing action types
The best way to import your action's TypeScript definitions depends on your bundler.
With Vite
With Vite, you can keep a separate actionTypes.ts
file (with the benefit of syntax highlighting), and use the ?raw
suffix to conveniently import the file contents as a string:
// actionTypes.ts
type createShape = ...
// App.jsx
import actionTypes from './actionTypes.ts?raw' // Import the whole file as raw string.
...
<CopilotProvider
context={{ actionTypes: actionTypes }}
// ...
/>
With any bundler
A universal method that works with any bundler is to store the type definitions in a plain string, then import it as a regular variable. However, you lose the syntax highlighting for the TypeScript definitions.
// actionTypes.ts
export const actionTypes = `
type createShape = ...
`
// App.jsx
import { actionTypes } from './actionTypes' // Import the string variable.
...
<CopilotProvider
context={{ actionTypes: actionTypes }}
// ...
/>