# Trilium Scripting System
> **Related:** [ARCHITECTURE.md](ARCHITECTURE.md) | [Script API Documentation](Script%20API/)
## Overview
Trilium features a **powerful scripting system** that allows users to extend and customize the application without modifying source code. Scripts are written in JavaScript and can execute both in the **frontend (browser)** and **backend (Node.js)** contexts.
## Script Types
### Frontend Scripts
**Location:** Attached to notes with `#run=frontendStartup` attribute
**Execution Context:** Browser environment
**Access:**
- Trilium Frontend API
- Browser APIs (DOM, localStorage, etc.)
- Froca (frontend cache)
- UI widgets
- No direct file system access
**Lifecycle:**
- `frontendStartup` - Run once when Trilium loads
- `frontendReload` - Run on every note context change
**Example:**
```javascript
// Attach to note with #run=frontendStartup
const api = window.api
// Add custom button to toolbar
api.addButtonToToolbar({
title: 'My Button',
icon: 'star',
action: () => {
api.showMessage('Hello from frontend!')
}
})
```
### Backend Scripts
**Location:** Attached to notes with `#run=backendStartup` attribute
**Execution Context:** Node.js server environment
**Access:**
- Trilium Backend API
- Node.js APIs (fs, http, etc.)
- Becca (backend cache)
- Database (SQL)
- External libraries (via require)
**Lifecycle:**
- `backendStartup` - Run once when server starts
- Event handlers (custom events)
**Example:**
```javascript
// Attach to note with #run=backendStartup
const api = require('@triliumnext/api')
// Listen for note creation
api.dayjs // Example: access dayjs library
api.onNoteCreated((note) => {
if (note.title.includes('TODO')) {
note.setLabel('priority', 'high')
}
})
```
### Render Scripts
**Location:** Attached to notes with `#customWidget` or similar attributes
**Purpose:** Custom note rendering/widgets
**Example:**
```javascript
// Custom widget for a note
class MyWidget extends api.NoteContextAwareWidget {
doRender() {
this.$widget = $('
')
.text('Custom widget content')
return this.$widget
}
}
module.exports = MyWidget
```
## Script API
### Frontend API
**Location:** `apps/client/src/services/frontend_script_api.ts`
**Global Access:** `window.api`
**Key Methods:**
```typescript
// Note Operations
api.getNote(noteId) // Get note object
api.getBranch(branchId) // Get branch object
api.getActiveNote() // Currently displayed note
api.openNote(noteId, activateNote) // Open note in UI
// UI Operations
api.showMessage(message) // Show toast notification
api.showDialog() // Show modal dialog
api.confirm(message) // Show confirmation dialog
api.prompt(message, defaultValue) // Show input prompt
// Tree Operations
api.getTree() // Get note tree structure
api.expandTree(noteId) // Expand tree branch
api.collapseTree(noteId) // Collapse tree branch
// Search
api.searchForNotes(searchQuery) // Search notes
api.searchForNote(searchQuery) // Get single note
// Navigation
api.openTabWithNote(noteId) // Open note in new tab
api.closeActiveTab() // Close current tab
api.activateNote(noteId) // Switch to note
// Attributes
api.getAttribute(noteId, type, name) // Get attribute
api.getAttributes(noteId, type, name) // Get all matching attributes
// Custom Widgets
api.addButtonToToolbar(def) // Add toolbar button
api.addCustomWidget(def) // Add custom widget
// Events
api.runOnNoteOpened(callback) // Note opened event
api.runOnNoteContentChange(callback) // Content changed event
// Utilities
api.dayjs // Date/time library
api.formatDate(date) // Format date
api.log(message) // Console log
```
### Backend API
**Location:** `apps/server/src/services/backend_script_api.ts`
**Access:** `require('@triliumnext/api')` or global `api`
**Key Methods:**
```typescript
// Note Operations
api.getNote(noteId) // Get note from Becca
api.getNoteWithContent(noteId) // Get note with content
api.createNote(parentNoteId, title) // Create new note
api.deleteNote(noteId) // Delete note
// Branch Operations
api.getBranch(branchId) // Get branch
api.createBranch(noteId, parentNoteId) // Create branch (clone)
// Attribute Operations
api.getAttribute(noteId, type, name) // Get attribute
api.createAttribute(noteId, type, name, value) // Create attribute
// Database Access
api.sql.getRow(query, params) // Execute SQL query (single row)
api.sql.getRows(query, params) // Execute SQL query (multiple rows)
api.sql.execute(query, params) // Execute SQL statement
// Events
api.onNoteCreated(callback) // Note created event
api.onNoteUpdated(callback) // Note updated event
api.onNoteDeleted(callback) // Note deleted event
api.onAttributeCreated(callback) // Attribute created event
// Search
api.searchForNotes(searchQuery) // Search notes
// Date/Time
api.dayjs // Date/time library
api.now() // Current date/time
// Logging
api.log(message) // Log message
api.error(message) // Log error
// External Communication
api.axios // HTTP client library
// Utilities
api.backup.backupNow() // Trigger backup
api.export.exportSubtree(noteId) // Export notes
```
## Script Attributes
### Execute Attributes
- `#run=frontendStartup` - Execute on frontend startup
- `#run=backendStartup` - Execute on backend startup
- `#run=hourly` - Execute every hour
- `#run=daily` - Execute daily
### Widget Attributes
- `#customWidget` - Custom note widget
- `#widget` - Standard widget integration
### Other Attributes
- `#disableVersioning` - Disable automatic versioning for this note
- `#hideChildrenOverview` - Hide children in overview
- `#iconClass` - Custom icon for note
## Entity Classes
### Frontend Entities
**FNote** (`apps/client/src/entities/fnote.ts`)
```typescript
class FNote {
noteId: string
title: string
type: string
mime: string
// Relationships
getParentNotes(): FNote[]
getChildNotes(): FNote[]
getBranches(): FBranch[]
// Attributes
getAttribute(type, name): FAttribute
getAttributes(type?, name?): FAttribute[]
hasLabel(name): boolean
getLabelValue(name): string
// Content
getContent(): Promise
// Navigation
open(): void
}
```
**FBranch**
```typescript
class FBranch {
branchId: string
noteId: string
parentNoteId: string
prefix: string
notePosition: number
getNote(): FNote
getParentNote(): FNote
}
```
**FAttribute**
```typescript
class FAttribute {
attributeId: string
noteId: string
type: 'label' | 'relation'
name: string
value: string
getNote(): FNote
getTargetNote(): FNote // For relations
}
```
### Backend Entities
**BNote** (`apps/server/src/becca/entities/bnote.ts`)
```typescript
class BNote {
noteId: string
title: string
type: string
mime: string
isProtected: boolean
// Content
getContent(): string | Buffer
setContent(content: string | Buffer): void
// Relationships
getParentNotes(): BNote[]
getChildNotes(): BNote[]
getBranches(): BBranch[]
// Attributes
getAttribute(type, name): BAttribute
getAttributes(type?, name?): BAttribute[]
setLabel(name, value): BAttribute
setRelation(name, targetNoteId): BAttribute
hasLabel(name): boolean
getLabelValue(name): string
// Operations
save(): void
markAsDeleted(): void
}
```
**BBranch**
```typescript
class BBranch {
branchId: string
noteId: string
parentNoteId: string
prefix: string
notePosition: number
getNote(): BNote
getParentNote(): BNote
save(): void
}
```
**BAttribute**
```typescript
class BAttribute {
attributeId: string
noteId: string
type: 'label' | 'relation'
name: string
value: string
getNote(): BNote
getTargetNote(): BNote // For relations
save(): void
}
```
## Script Examples
### Frontend Examples
**1. Custom Toolbar Button**
```javascript
// #run=frontendStartup
api.addButtonToToolbar({
title: 'Export to PDF',
icon: 'file-export',
action: async () => {
const note = api.getActiveNote()
if (note) {
await api.runOnBackend('exportToPdf', [note.noteId])
api.showMessage('Export started')
}
}
})
```
**2. Auto-Save Reminder**
```javascript
// #run=frontendStartup
let saveTimer
api.runOnNoteContentChange(() => {
clearTimeout(saveTimer)
saveTimer = setTimeout(() => {
api.showMessage('Remember to save your work!')
}, 300000) // 5 minutes
})
```
**3. Note Statistics Widget**
```javascript
// #customWidget
class StatsWidget extends api.NoteContextAwareWidget {
doRender() {
this.$widget = $('