| name | building-logseq-plugins |
| version | 1.0.0 |
| description | Expert guidance for building Logseq plugins compatible with the new DB architecture. Auto-invokes when users want to create Logseq plugins, work with the Logseq Plugin API, extend Logseq functionality, or need help with plugin development for DB-based graphs. Covers plugin structure, API usage, and DB-specific considerations. |
| allowed-tools | Read, Grep, Glob, WebFetch, WebSearch, Write, Edit, Bash |
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
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
{
"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
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
// 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
// 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+)
// 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
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
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
// 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
logseq.Editor.registerSlashCommand('Insert Book Template', async () => {
await logseq.Editor.insertAtEditingCursor(`
- [[New Book]] #Book
author::
rating::
status:: To Read
`)
})
Block Context Menu
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
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
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
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
Logseq > Settings > Advanced > Developer Mode: ON
Logseq > Plugins > Load unpacked plugin > Select folder