Tutorial
Create a frontend Pine extension from scratch.
Last updated
Create a frontend Pine extension from scratch.
Last updated
Extensions are sandboxed, client-side applications that extend Pine's capabilities. Extensions can be used to display information from third-party tools directly within the Pine interface.
Pine's platform supports standard web technologies, meaning that creating extensions only requires knowledge of JavaScript, HTML, and CSS. The only limitation is that all of your code must be self-contained and bundled into a single index.html
file.
In this tutorial, we'll create a Pine extension from scratch. The extension will feature multiple routes and will simply display the ID of the currently selected card.
Before getting started, please ensure that you meet the following prerequisites:
Using your terminal, run the following to create your project:
Once complete, navigate to your project and install the dependencies for two additional libraries:
@radix-ui/themes
provides fully-styled components for building the application (Pine uses this library internally, so this will ensure consistency with the main interface).
To start with a clean slate, let's delete the existing App.css
file and replace the contents of the App.tsx
file with the following:
We'll also replace the contents of the index.css
file with just the following:
You can then start your application in development using:
@pinecards/client
At the top of your App.tsx
file, import the following:
Next, we'll define what our application state should look like:
The path
will be used to navigate between our extension's routes.
The theme
property will be used to keep the application theme in sync with the theme data received from Pine.
The result
property will be used to store the card identifier that we extract whenever Pine changes routes.
We'll hook this up inside of the App
component by using the previously imported useState
hook to define the state and the useCallback
hook to allow for partial state updates:
Next, we'll create a useEffect
block and use the client
to communicate with Pine to get the current theme data and listen for future changes.
We'll create a separate useEffect
block to listen for route changes.
Instead of returning a URL that needs to be manually parsed, Pine returns the path
template and the params
that were used to populate that template.
We can take advantage of this by implementing a switch
statement that handles paths that involve card identifiers, and assign the route.params.id
to our result
.
@radix-ui/themes
Let's import the Radix CSS file and the necessary components that we'll need:
Next, we'll render the ThemeProvider
and pass through the following properties:
scaling
, which we'll set to a value of "90%"
to ensure that the default Radix components are appropriately scaled to the rest of the interface.
appearance
, which will be assigned state.theme.mode
to keep the user's theme preferences in sync with the extensions.
accentColor
, which will be assigned state.theme.color
to keep the user's color preferences in sync with the extension.
While we've been previewing our changes in the web browser, it's also possible to test our extensions inside of Pine against workspace data.
At the bottom of this view, enter the URL of your local development server to preview your application inside the sidebar.
If you encounter issues while running your app, you can click on the vertical ellipsis again to reveal options for reloading and removing the preview extension.
Once installed, update your vite.config.ts
and include viteSingleFile
as a plugin:
You can then finally bundle your project into a single HTML file by running the following:
Next, we'll upload the bundled dist/index.html
file to Pine.
Congratulations! You have now made a simple extension that displays the card ID of any currently selected card! You now have the foundations to make complex projects extending Pine.
You have the latest stable version installed.
You have the latest stable version of installed.
You are familiar with and .
@pinecards/client
contains logic for communicating with Pine via .
We'll want to execute connectSDKClient
as soon as possible in the global scope of the application. Calling this function will create a client
that we can use to communicate with Pine.
By default, Pine will throw an IPCError
if a response hasn't been received within 5 seconds (due to a possible failed connection). You can learn more in .
From this point, you can freely use the components described in Radix's . For example, we'll add a Button
for navigating between routes and we'll use the Callout
component to display the Card ID that we retrieved in our useEffect
block.
To preview an extension, navigate to and click the sidebar icon in the top-right corner to open the extensions sidebar. Then click the vertical ellipsis next to the integrations title to reveal a dropdown menu. Select the preview integration option.
Once you're satisfied with how your extension behaves, we'll now bundle into a single HTML file that is suitable for production. We'll use the plugin to take care of this. Install it in your project:
To upload the index.html
file contained with the dist
directory, navigate to the integration settings in Pine (if you haven't created an integration, follow the steps outlined ). Navigate to the bottom of the integration page until you reach the extensions section. Under sidebar extension, click the upload button and select your index.html
file.
The bundled index.html
file should not exceed 2 megabytes. Learn more in .
Click the create/update button at the bottom of the page to finalize your upload. Once you have installed the integration (outlined ), it will appear in your Pine's integrations sidebar.