Building Logseq Plugins
When to Use This Skill
This skill auto-invokes when:
- •User wants to create a Logseq plugin
- •Questions about the Logseq Plugin API
- •Working with logseq.Editor, logseq.DB, logseq.App namespaces
- •Slash command registration
- •Plugin settings schema definition
- •DB vs MD version compatibility for plugins
- •User mentions "logseq plugin", "logseq extension", "@logseq/libs"
You are an expert in Logseq plugin development, with special focus on DB-version compatibility.
Plugin Architecture Overview
Logseq plugins run in sandboxed iframes and communicate with the main app via the Plugin API.
Plugin Structure
code
my-logseq-plugin/ ├── package.json # Plugin manifest ├── index.html # Entry point ├── index.js # Main logic (or TypeScript) ├── icon.png # Plugin icon (optional) └── README.md # Documentation
package.json Manifest
json
{
"name": "logseq-my-plugin",
"version": "1.0.0",
"main": "index.html",
"logseq": {
"id": "my-plugin-id",
"title": "My Plugin",
"icon": "./icon.png",
"description": "Plugin description"
}
}
Plugin API Basics
Initialization
javascript
import '@logseq/libs'
async function main() {
console.log('Plugin loaded')
// Register commands, settings, etc.
logseq.Editor.registerSlashCommand('My Command', async () => {
// Command logic
})
}
logseq.ready(main).catch(console.error)
Key API Namespaces
| Namespace | Purpose |
|---|---|
logseq.Editor | Block/page manipulation |
logseq.DB | Database queries |
logseq.App | App-level operations |
logseq.UI | UI components |
logseq.Settings | Plugin settings |
DB-Version Specific APIs
Working with Properties
javascript
// Get block with properties (DB version)
const block = await logseq.Editor.getBlock(blockUuid, {
includeChildren: true
})
// Properties are structured differently in DB version
// Access via block.properties object
const author = block.properties?.author
// Set property value
await logseq.Editor.upsertBlockProperty(blockUuid, 'author', 'John Doe')
Working with Classes/Tags
javascript
// Get blocks with a specific tag/class const books = await logseq.DB.datascriptQuery(` [:find (pull ?b [*]) :where [?b :block/tags ?t] [?t :block/title "Book"]] `) // Add tag to block await logseq.Editor.upsertBlockProperty(blockUuid, 'tags', ['Book', 'Fiction'])
Enhanced Block Properties APIs (2024+)
javascript
// New APIs for DB version
await logseq.Editor.getBlockProperties(blockUuid)
await logseq.Editor.setBlockProperties(blockUuid, {
author: 'Jane Doe',
rating: 5,
published: '2024-01-15'
})
Datalog Queries in Plugins
Basic Query
javascript
const results = await logseq.DB.datascriptQuery(` [:find ?title :where [?p :block/title ?title] [?p :block/tags ?t] [?t :db/ident :logseq.class/Page]] `)
Query with Parameters
javascript
const results = await logseq.DB.datascriptQuery(` [:find (pull ?b [*]) :in $ ?tag-name :where [?b :block/tags ?t] [?t :block/title ?tag-name]] `, ['Book'])
DB vs MD Query Differences
javascript
// MD version - file-based queries const mdQuery = ` [:find ?content :where [?b :block/content ?content] [?b :block/page ?p] [?p :block/name "my-page"]] ` // DB version - entity-based queries const dbQuery = ` [:find ?title :where [?b :block/title ?title] [?b :block/tags ?t] [?t :block/title "My Tag"]] `
UI Components
Slash Commands
javascript
logseq.Editor.registerSlashCommand('Insert Book Template', async () => {
await logseq.Editor.insertAtEditingCursor(`
- [[New Book]] #Book
author::
rating::
status:: To Read
`)
})
Block Context Menu
javascript
logseq.Editor.registerBlockContextMenuItem('Convert to Task', async (e) => {
const block = await logseq.Editor.getBlock(e.uuid)
await logseq.Editor.upsertBlockProperty(e.uuid, 'tags', ['Task'])
await logseq.Editor.upsertBlockProperty(e.uuid, 'status', 'Todo')
})
Settings Schema
javascript
logseq.useSettingsSchema([
{
key: 'apiKey',
type: 'string',
title: 'API Key',
description: 'Your API key for the service',
default: ''
},
{
key: 'enableFeature',
type: 'boolean',
title: 'Enable Feature',
default: true
},
{
key: 'theme',
type: 'enum',
title: 'Theme',
enumChoices: ['light', 'dark', 'auto'],
default: 'auto'
}
])
DB-Version Compatibility Checklist
When building plugins for DB version:
- • Use
:block/titleinstead of:block/contentfor page names - • Handle structured properties (not just strings)
- • Support typed property values (numbers, dates, checkboxes)
- • Use tag-based queries instead of namespace queries
- • Test with both DB and MD graphs if supporting both
- • Handle the unified node model (pages ≈ blocks)
- • Use new block properties APIs when available
Common Patterns
Feature Detection
javascript
async function isDBGraph() {
// Check if current graph is DB-based
const graph = await logseq.App.getCurrentGraph()
return graph?.name?.includes('db-') || graph?.type === 'db'
}
async function main() {
const isDB = await isDBGraph()
if (isDB) {
// DB-specific initialization
} else {
// MD-specific initialization
}
}
Property Type Handling
javascript
function formatPropertyValue(value, type) {
switch (type) {
case 'date':
return new Date(value).toISOString().split('T')[0]
case 'number':
return Number(value)
case 'checkbox':
return Boolean(value)
case 'node':
return `[[${value}]]`
default:
return String(value)
}
}
Development Workflow
- •Setup: Clone logseq-plugin-template or start fresh
- •Develop: Use
npm run devfor hot reload - •Test: Load unpacked plugin in Logseq
- •Build:
npm run buildfor production - •Publish: Submit to Logseq Marketplace
Loading Development Plugin
code
Logseq > Settings > Advanced > Developer Mode: ON Logseq > Plugins > Load unpacked plugin > Select folder