This guide covers the experimental APIs available in the Logseq Plugin SDK. These APIs provide advanced functionality for creating custom renderers, loading external scripts, and accessing internal utilities.
⚠️ WARNING: These are experimental features that may change at any time. Plugins using these APIs may not be supported on the Marketplace temporarily.
The Experiments API is accessed via logseq.Experiments and provides:
Access React and ReactDOM from the Logseq host environment.
logseq.Experiments.ReactReturns the React instance from the host scope.
const React = logseq.Experiments.React
logseq.Experiments.ReactDOMReturns the ReactDOM instance from the host scope.
const ReactDOM = logseq.Experiments.ReactDOM
const React = logseq.Experiments.React
const ReactDOM = logseq.Experiments.ReactDOM
// Use React to create components
const MyComponent = React.createElement('div', null, 'Hello from plugin!')
Access internal Logseq components for advanced UI integration.
logseq.Experiments.Components.EditorA page editor component that can render Logseq page content.
Type: (props: { page: string } & any) => any
Parameters:
page (string): The page name to render
const Editor = logseq.Experiments.Components.Editor
// Render a page editor
const editor = Editor({ page: 'My Page Name' })
ClojureScript interop utilities for data conversion between JavaScript and ClojureScript.
logseq.Experiments.UtilsProvides conversion utilities:
toClj(input: any)Convert JavaScript data to ClojureScript data structures.
const cljData = logseq.Experiments.Utils.toClj({ key: 'value' })
jsxToClj(input: any)Convert JSX/JavaScript objects to ClojureScript, preserving JSX structures.
const cljData = logseq.Experiments.Utils.jsxToClj(<div>Content</div>)
toJs(input: any)Convert ClojureScript data structures to JavaScript.
const jsData = logseq.Experiments.Utils.toJs(cljData)
toKeyword(input: any)Convert a string to a ClojureScript keyword.
const keyword = logseq.Experiments.Utils.toKeyword('my-key')
toSymbol(input: any)Convert a string to a ClojureScript symbol.
const symbol = logseq.Experiments.Utils.toSymbol('my-symbol')
logseq.Experiments.loadScripts(...scripts: string[])Dynamically load external scripts into the Logseq environment.
Parameters:
scripts (string[]): Array of script URLs or relative pathsReturns: Promise<void>
Behavior:
Scripts are loaded in order
// Load external library
await logseq.Experiments.loadScripts(
'https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.min.js'
)
// Load local script from plugin resources
await logseq.Experiments.loadScripts('./my-script.js')
// Load multiple scripts
await logseq.Experiments.loadScripts(
'https://cdn.example.com/lib1.js',
'https://cdn.example.com/lib2.js',
'./local-script.js'
)
Register a custom renderer for code blocks with specific language tags.
logseq.Experiments.registerFencedCodeRenderer(lang: string, opts: object)Parameters:
lang (string): The language identifier for the code block (e.g., 'mermaid', 'chart')opts (object):
render (function, required): Render function that receives propsedit (boolean, optional): Whether the block is editablebefore (function, optional): Async function to run before renderingsubs (string[], optional): Subscriptions to state changesRender Props:
content (string): The content of the code block
// Register a custom code block renderer
logseq.Experiments.registerFencedCodeRenderer('my-chart', {
edit: false,
before: async () => {
// Load dependencies before rendering
await logseq.Experiments.loadScripts(
'https://cdn.jsdelivr.net/npm/chart.js'
)
},
render: (props) => {
const React = logseq.Experiments.React
return React.createElement('div', {
ref: (el) => {
if (el) {
// Parse content and render chart
const config = JSON.parse(props.content)
new Chart(el, config)
}
}
})
}
})
Usage in Logseq:
```my-chart
{
"type": "bar",
"data": {
"labels": ["A", "B", "C"],
"datasets": [{"data": [10, 20, 30]}]
}
}
```
Register a renderer that runs continuously in the background (daemon).
logseq.Experiments.registerDaemonRenderer(key: string, opts: object)Parameters:
key (string): Unique identifier for the daemon rendereropts (object):
render (function, required): Render functionsub (string[], optional): Subscriptions to state changes
// Register a daemon renderer for persistent UI
logseq.Experiments.registerDaemonRenderer('my-status-bar', {
sub: ['ui/theme', 'ui/sidebar-open'],
render: (props) => {
const React = logseq.Experiments.React
return React.createElement('div', {
style: {
position: 'fixed',
bottom: 0,
right: 0,
padding: '10px',
background: '#333',
color: '#fff'
}
}, 'Status: Active')
}
})
Register a custom renderer for specific routes in Logseq.
logseq.Experiments.registerRouteRenderer(key: string, opts: object)Parameters:
key (string): Unique identifier for the route rendereropts (object):
path (string, required): Route path (e.g., '/my-plugin-page')render (function, required): Render functionname (string, optional): Display name for the routesubs (string[], optional): Subscriptions to state changes
// Register a custom route
logseq.Experiments.registerRouteRenderer('my-custom-page', {
path: '/my-plugin-dashboard',
name: 'Dashboard',
subs: ['ui/theme'],
render: (props) => {
const React = logseq.Experiments.React
return React.createElement('div', {
className: 'my-plugin-dashboard'
}, [
React.createElement('h1', null, 'Plugin Dashboard'),
React.createElement('p', null, 'Custom content here')
])
}
})
// Navigate to the route
logseq.App.pushState('page', { name: 'my-plugin-dashboard' })
Enhance external libraries that Logseq uses (like KaTeX for math rendering).
logseq.Experiments.registerExtensionsEnhancer(type: string, enhancer: function)Parameters:
type ('katex' | 'codemirror'): The extension type to enhanceenhancer (function): Async function that receives the library instance and can modify itReturns: Promise<void>
// Enhance KaTeX with custom macros
logseq.Experiments.registerExtensionsEnhancer('katex', async (katex) => {
// Add custom KaTeX macros
katex.macros = {
...katex.macros,
'\\RR': '\\mathbb{R}',
'\\NN': '\\mathbb{N}',
'\\ZZ': '\\mathbb{Z}'
}
console.log('KaTeX enhanced with custom macros')
})
logseq.Experiments.pluginLocalAccess the internal plugin instance (PluginLocal) for advanced operations.
Type: PluginLocal
const pluginLocal = logseq.Experiments.pluginLocal
// Access plugin-specific internal state
console.log('Plugin ID:', pluginLocal.id)
logseq.Experiments.invokeExperMethod(type: string, ...args: any[])Directly invoke experimental methods from the host scope.
Parameters:
type (string): Method name (converted to snake_case)...args: Arguments to pass to the methodReturns: any
// Invoke a custom experimental method
const result = logseq.Experiments.invokeExperMethod(
'someExperimentalFeature',
arg1,
arg2
)
Here's a complete example combining multiple APIs:
import '@logseq/libs'
async function main() {
console.log('Chart Plugin Loaded')
// Register fenced code renderer for charts
logseq.Experiments.registerFencedCodeRenderer('chart', {
edit: false,
before: async () => {
// Load Chart.js before rendering
await logseq.Experiments.loadScripts(
'https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.min.js'
)
},
render: (props) => {
const React = logseq.Experiments.React
return React.createElement('div', null, [
React.createElement('canvas', {
ref: (canvas) => {
if (canvas && window.Chart) {
try {
const config = JSON.parse(props.content)
new window.Chart(canvas, config)
} catch (e) {
console.error('Chart rendering error:', e)
}
}
}
})
])
}
})
}
logseq.ready(main).catch(console.error)
Usage:
```chart
{
"type": "line",
"data": {
"labels": ["Jan", "Feb", "Mar", "Apr"],
"datasets": [{
"label": "Sales",
"data": [10, 20, 15, 30],
"borderColor": "rgb(75, 192, 192)"
}]
}
}
```
before hooks to ensure they're readyFor questions and issues:
Remember: These are experimental features. Use at your own risk and always test thoroughly!