Extension Development
Positron is compatible with VS Code extensions so you can create extensions as you would for VS Code. You can use Positron to develop your extension and run it in a new Extension Development Host window.
Prerequisites
To follow these docs and run the examples, you’ll need:
- Basic familiarity with TypeScript/JavaScript and npm
- An up-to-date version of Positron installed
- Node.js (version 18 or greater)
These docs use the @posit-dev/positron npm package for type safety and IntelliSense.
The easiest way to follow along is to download the Positron extension template and add the examples to it as needed.
For more comprehensive setup instructions see the VS Code documentation on developing an extension.
What can you build?
The Positron API provides access to Positron-specific features like running code in active R or Python sessions, utilizing Positron’s preview pane, or contributing new database connections.
The following are the different sections of the API and how they’re used.
Namespace | Typical use case |
---|---|
positron.window |
Preview URLs and HTML, monitor console layout |
positron.runtime |
Execute Python or R code in the active console, enumerate sessions, inspect variables |
positron.connections |
Add database / service drivers with custom credential UI |
positron.languages |
Provide statement range & help topic providers for new languages |
positron.methods |
Call low-level RPCs or show purpose-built dialogs |
positron.environment |
Inspect environment variable contributions from all installed extensions |
Setting up your extension
The easiest way to start an extension is to use the Positron extension template.
Alternatively, you can use VS Code’s extension template system to scaffold out a plain VS Code extension and then add Positron functionality to that by installing the Positron npm package.
npm install @posit-dev/positron
Key files
There are two files that are critical when setting up your extension.
Extension script (extension.ts
)
import * as vscode from 'vscode';
import { tryAcquirePositronApi } from '@posit-dev/positron';
export function activate(context: vscode.ExtensionContext) {
// Register a command
const disposable = vscode.commands.registerCommand('myExtension.helloWorld', () => {
const positron = tryAcquirePositronApi();
if (positron) {
.window.showInformationMessage('Hello from Positron!');
vscode// Use Positron features here
else {
} .window.showInformationMessage('Hello from VS Code!');
vscode
};
})
.subscriptions.push(disposable);
context
}
export function deactivate() {}
The activate()
function is run when your extension is activated. It’s where you setup the code behind commands and other extension contributions.
Extension manifest (package.json
)
{
"name": "my-extension",
"displayName": "My Extension",
"version": "0.0.1",
"engines": {
"vscode": "^1.74.0"
},
"activationEvents": [],
"main": "./out/extension.js",
"contributes": {
"commands": [
{
"command": "myExtension.helloWorld",
"title": "Hello World"
}
]
},
"dependencies": {
"@posit-dev/positron": "^0.1.0"
}
}
Your extension’s package.json
file uses custom extension fields to declare what your extension contributes. It’s important to note that you need to add the contribution (in this case the command myExtension.helloWorld
) to both the Typescript/Javascript code and the package.json
.
Now users can run your command from the Command Palette (Cmd/Ctrl+Shift+P).
Detecting Positron
You have two ways to detect if your extension is running in Positron:
Option 1: Context keys
Use the isPositron
context key in your extension manifest for simple detection:
"commands": [
{
"category": "My Extension",
"command": "myExtension.myCommand",
"title": "My Extension Command",
"enablement": "isPositron"
}
]
This enables commands, keybindings, and menu items to be visible only in Positron.
Option 2: Positron API
Detect Positron in your code:
import { tryAcquirePositronApi } from '@posit-dev/positron';
const positron = tryAcquirePositronApi();
if (positron) {
// Positron features are available with full type safety
}
Alternatively, if you just need to check but don’t need the API you can use inPositron()
:
import { inPositron } from '@posit-dev/positron';
if (inPositron()) {
// Behavior specific for Positron
}
Examples
Each example shows how to use a Positron feature inside a command.
Prefer to look at examples in a full extension context? Check out the Positron API showcase extension.
Display a web page
const disposable = vscode.commands.registerCommand('myExtension.showWebPage', () => {
const positron = tryAcquirePositronApi();
if (positron) {
const url = vscode.Uri.parse('https://example.com');
.window.previewUrl(url);
positron
}; })
Add this command to your package.json:
{
"command": "myExtension.showWebPage",
"title": "Show Web Page"
}
Run code in the console
Execute code in the active Python or R session:
const disposable = vscode.commands.registerCommand('myExtension.runCode', () => {
const positron = tryAcquirePositronApi();
if (positron) {
// Run Python code
.runtime.executeCode('python', 'print("Hello from extension!")', true);
positron
// Or run R code
// positron.runtime.executeCode('r', 'print("Hello from R")', true);
}; })
The third parameter (true
) means the code appears in the console history.
Add a database connection
Register a custom database type with a working mock example:
// In your activate function
export function activate(context: vscode.ExtensionContext) {
const positron = tryAcquirePositronApi();
if (positron) {
const disposable = positron.connections.registerConnectionDriver({
: 'mockdb',
driverId: {
metadata: 'python',
languageId: 'Mock Database',
name: [
inputs
{: 'database',
id: 'Database Name',
label: 'string',
type: 'test_db'
value
}
],
}: (inputs) => {
generateCode// If any of the inputs are an ID, use that as the name
const dbName = inputs.find(i => i.id === 'database')?.value || 'test_db';
return mockDatabaseCode(dbName);
,
}: async (code) => {
connectawait positron.runtime.executeCode('python', code, true);
};
}).subscriptions.push(disposable);
context
} }
Mock database code
In this example we simulate a database connection with a basic Python class. Swap this logic out for your use case!
// Mock database code generator.
const mockDatabaseCode = (dbName: string) => `
# Mock database connection
class MockDatabase:
def __init__(self, name):
self.name = name
self.tables = ['users', 'products', 'orders']
print(f"Connected to mock database: {name}")
print(f"Available tables: {', '.join(self.tables)}")
def query(self, sql):
return f"Mock result for: {sql}"
# Create connection
mock_db = MockDatabase("${dbName}")
print("\\nConnection successful! Try: mock_db.query('SELECT * FROM users')")
`;
The mockDatabaseCode
function generates Python code that creates a simple mock database class. This lets you test the connection without installing a real database.
Advanced examples
These examples demonstrate more advanced integrations with Positron’s data science features:
Stream output with observers
Capture and process output from long-running computations as they execute:
import * as vscode from "vscode";
export async function runLongTask(code: string) {
const positron = tryAcquirePositronApi();
if (!positron) return;
// Create an observer to handle output events
const observer = {
: () => vscode.window.showInformationMessage("Analysis started"),
onStarted: (message) => {
onOutput// Process output chunks as they arrive
console.log("Output:", message.text || message);
,
}: (error) => {
onErrorconsole.error("Runtime error:", error);
.window.showErrorMessage(`Error: ${error.message}`);
vscode,
}: () => vscode.window.showInformationMessage("Analysis complete"),
onFinished;
}
// Execute code with streaming output
await positron.runtime.executeCode(
"python",
,
codetrue, // show in console
false, // not an evaluation
undefined,
undefined,
observer;
) }
Use observers to process output progressively without blocking the UI. This approach works especially well for monitoring long-running analyses or streaming results.
Best practices
Performance
- Batch API calls when possible; use
getSessionVariables()
instead of multiple individual lookups. - All API methods are asynchronous; always use
await
or handle promises appropriately.
Testing
- The Positron extension template has simple testing infrastructure setup for you that can be extended to fit your extension’s needs.
- The Positron showcase extension has a more fleshed out testing implementation.
- Refer to the VS Code testing docs for more information.
Resource management
- Clean up event listeners in your extension’s
deactivate()
function. - Add disposables to
context.subscriptions
for automatic cleanup.
Security
- Set
localResourceRoots
when creating webviews. - Use content security policies for webviews that load external content.
- Never expose sensitive data in webview messages.
Version compatibility
You can ensure your extension only activates in versions of Positron that you explicitly support by declaring a suitable version range in your package.json
.
Positron versioning
Positron follows a calendar versioning scheme, while VS Code uses semantic versioning. Because of this, common range operators such as the caret (^
) behave differently on each platform:
- VS Code:
major.minor.patch
- A caret range stays within the current major version;
^1.74.0
expands to>=1.74.0
and<2.0.0
. - Stepping up to
2.0.0
can therefore introduce breaking changes.
- A caret range stays within the current major version;
- Positron:
YYYY.M.P
(year · month · patch)- Under calendar versioning, the caret simply means “this version or anything newer”;
^2025.6.0
translates to>=2025.6.0
with no upper bound. - Entering a new year (
2026.x.x
) is not automatically breaking—always consult the release notes.
- Under calendar versioning, the caret simply means “this version or anything newer”;
Specifying Positron version requirements
Add a positron
entry under the engines
section of your package.json
to declare the minimum build that your extension supports.
{
"engines": {
"vscode": "^1.74.0", // VS Code 1.74.0 or later
"positron": "^2025.6.0" // Positron 2025.6.0 or later
}
}
@posit-dev/positron
versioning
You can check the version compatibility table in the @posit-dev/positron
package to check for any breaking changes you may need to be aware of.
Next steps
Resources
- Start quickly with the Positron extension template.
- See more examples of the available API in the showcase extension.
- Complete type documentation is available on npm.
- Browse the source code of the API package repository.
Get help
- Ask questions in GitHub discussions.
- File bugs in the API repository.
Start with the extension template to get a working setup quickly. Then add features one at a time as you learn the API.