building-logseq-plugins
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.
When & Why to Use This Skill
This Claude skill serves as a specialized technical assistant for developers building Logseq plugins, with a primary focus on the transition to the new DB-based architecture. It provides comprehensive guidance on the Logseq Plugin API, Datalog queries, and UI component integration. By offering architectural patterns, compatibility checklists, and code snippets, it streamlines the development lifecycle, helping users create robust extensions that work seamlessly across different Logseq graph versions.
Use Cases
- Scenario 1: Migrating legacy Logseq plugins to the new DB architecture by implementing structured property handling and entity-based Datalog queries.
- Scenario 2: Building custom user interface elements within Logseq, such as slash commands, block context menu items, and complex settings schemas.
- Scenario 3: Developing advanced data management tools that utilize the logseq.DB and logseq.Editor namespaces to automate block manipulation and knowledge retrieval.
- Scenario 4: Implementing feature detection and compatibility layers to ensure plugins function correctly on both Markdown-based and Database-based Logseq graphs.
| name | building-logseq-plugins |
|---|---|
| version | 1.0.0 |
| description | > |
| 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