docs/addons/addons-overview.md
Wealthfolio addons are TypeScript modules that extend the application's functionality. This guide covers how to build, test, and distribute addons.
New to addon development? Start with our Quick Start Guide to create your first addon.
Addons are TypeScript/React-based extensions that provide access to Wealthfolio's financial data and UI system.
Technical Foundation Each addon is a JavaScript function that receives an
AddonContext object with access to APIs, UI components, and event system.
Integration Capabilities Addons can register new navigation items, routes, and components that integrate directly into Wealthfolio's interface.
Development Environment Built with TypeScript, React, and modern web APIs. Includes hot-reload development server and comprehensive type definitions.
┌─────────────────────────────────────────────────────────────────┐
│ Wealthfolio Host Application │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Addon Runtime │ │ Permission │ │ API Bridge │ │
│ │ │ │ System │ │ │ │
│ │ • Load/Unload │ │ • Detection │ │ • Type Bridge │ │
│ │ • Lifecycle │ │ • Validation │ │ • Domain APIs │ │
│ │ • Context Mgmt │ │ • Enforcement │ │ • Scoped Access │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ Individual Addons │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Addon A │ │ Addon B │ │ Addon C │ │ Addon D │ │
│ │ │ │ │ │ │ │ │ │
│ │ enable() │ │ enable() │ │ enable() │ │ enable() │ │
│ │ disable() │ │ disable() │ │ disable() │ │ disable() │ │
│ │ UI/Routes │ │ UI/Routes │ │ UI/Routes │ │ UI/Routes │ │
│ │ API Calls │ │ API Calls │ │ API Calls │ │ API Calls │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Every addon exports an enable function that receives a context object:
import type { AddonContext } from '@wealthfolio/addon-sdk';
import { Icons } from '@wealthfolio/ui';
export default function enable(ctx: AddonContext) {
// Access financial data
const accounts = await ctx.api.accounts.getAll();
// Add navigation item
const sidebarItem = ctx.sidebar.addItem({
id: 'my-addon',
icon: <Icons.Blocks className="h-5 w-5" />,
label: 'My Tool',
route: '/my-addon'
});
// Register route
ctx.router.add({
path: '/my-addon',
component: MyComponent
});
// Listen to events
const unlisten = ctx.api.events.portfolio.onUpdateComplete(() => {
// Handle portfolio updates
});
// Cleanup function
return {
disable() {
sidebarItem.remove();
unlisten();
}
};
}
Addons operate under a permission-based security model with three stages:
During installation, addon code is scanned for API usage patterns:
// This pattern is detected:
const accounts = await ctx.api.accounts.getAll();
// Detected permission: accounts.getAll
| Category | Risk Level | Functions |
|---|---|---|
accounts | High | getAll, create |
portfolio | High | getHoldings, update, recalculate |
activities | High | getAll, search, create, update, import |
market-data | Low | searchTicker, sync, getProviders |
assets | Medium | getProfile, updateProfile, updateDataSource |
quotes | Low | update, getHistory |
performance | Medium | calculateHistory, calculateSummary |
goals | Medium | getAll, create, update, updateAllocations |
settings | Medium | get, update, backupDatabase |
files | Medium | openCsvDialog, openSaveDialog |
events | Low | onDrop, onUpdateComplete, onSyncStart |
secrets | High | set, get, delete |
During installation, users see both declared and detected permissions, then approve or reject the addon installation.
The addon context provides access to 14 domain-specific APIs:
interface AddonContext {
sidebar: SidebarAPI;
router: RouterAPI;
onDisable: (callback: () => void) => void;
api: {
accounts: AccountsAPI;
portfolio: PortfolioAPI;
activities: ActivitiesAPI;
market: MarketAPI;
assets: AssetsAPI;
quotes: QuotesAPI;
performance: PerformanceAPI;
exchangeRates: ExchangeRatesAPI;
goals: GoalsAPI;
contributionLimits: ContributionLimitsAPI;
settings: SettingsAPI;
files: FilesAPI;
events: EventsAPI;
secrets: SecretsAPI;
};
}
npm install @wealthfolio/addon-sdk @wealthfolio/ui react react-dom
npm install -D @wealthfolio/addon-dev-tools typescript vite
The development tools include a hot-reload server:
# Start development server
npm run dev:server
# Available on localhost:3001-3003
# Auto-discovered by Wealthfolio
Development Server Structure:
├─ /health # Health check
├─ /status # Build status
├─ /manifest.json # Addon manifest
└─ /addon.js # Built addon code
hello-world-addon/
├── src/
│ ├── addon.tsx # Main addon entry point
│ ├── components/ # React components
│ ├── hooks/ # React hooks
│ ├── pages/ # Addon pages
│ ├── utils/ # Utility functions
│ └── types/ # Type definitions
├── assets/ # Static assets (optional)
├── dist/ # Built files (generated)
├── manifest.json # Addon metadata and permissions
├── package.json # NPM package configuration
├── vite.config.ts # Build configuration
├── tsconfig.json # TypeScript configuration
└── README.md # Documentation
{
"id": "my-addon",
"name": "My Addon",
"version": "1.0.0",
"main": "dist/addon.js",
"description": "Addon description",
"author": "Your Name",
"permissions": ["accounts.getAll", "portfolio.getHoldings"],
"sdkVersion": "1.0.0"
}
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ │ │ │ │ │ │ │
│ ZIP File │───▶│ Extract │───▶│ Validate │───▶│ Analyze │
│ │ │ │ │ │ │ Permissions │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ │ │ │ │ │
│ Running │◀───│ Enable │◀───│ Load │◀─────────────┘
│ │ │ │ │ │
└─────────────┘ └─────────────┘ └─────────────┘
Each addon receives an isolated context with scoped secret storage:
// Addon "my-addon" accessing secrets
await ctx.api.secrets.set("api-key", "value");
// Stored as: "addon_my-addon_api-key"
Addons have access to Wealthfolio's UI component library:
import { Button, Card, Dialog, Input, Table } from '@wealthfolio/ui';
import { AmountDisplay, GainAmount, CurrencyInput } from '@wealthfolio/ui/financial';
import { TrendingUp, DollarSign } from 'lucide-react';
function MyComponent() {
return (
<Card className="p-6">
<div className="flex items-center space-x-2">
<TrendingUp className="h-4 w-4" />
<span>Portfolio Growth</span>
</div>
<div className="mt-4">
<AmountDisplay value={1234.56} currency="USD" />
<GainAmount value={123.45} percentage={5.2} />
</div>
</Card>
);
}
Available libraries:
components/financial) for amounts, gains, and
currency inputsStandard Vite configuration externalizes React:
// vite.config.ts
export default defineConfig({
plugins: [react()],
build: {
lib: {
entry: "src/addon.tsx",
fileName: () => "addon.js",
formats: ["es"],
},
rollupOptions: {
external: ["react", "react-dom"],
plugins: [
externalGlobals({
react: "React",
"react-dom": "ReactDOM",
}),
],
},
},
});
{
"scripts": {
"build": "vite build",
"dev": "vite build --watch",
"dev:server": "wealthfolio dev",
"clean": "rm -rf dist",
"package": "mkdir -p dist && zip -r dist/$npm_package_name-$npm_package_version.zip manifest.json dist/ assets/ README.md",
"bundle": "pnpm clean && pnpm build && pnpm package",
"lint": "tsc --noEmit",
"type-check": "tsc --noEmit"
}
}
PermissionError thrown for unauthorized API callsUsers can install addons directly from ZIP files. To publish your addon in the Wealthfolio Store, contact [email protected].