Plugins
Plugins teach Lix how to understand specific file formats by detecting changes at the entity level and reconstructing files from entity states.
What Plugins Do
A plugin handles two core responsibilities:
1. Detect changes
- Compare old and new file content
- Identify which entities changed (added, modified, or deleted)
- Return detected changes with entity IDs and snapshots
2. Apply changes
- Reconstruct a file from entity states
- Serialize entities back to the original file format
- Preserve structure and formatting where possible
Optional: Render diffs
- Generate HTML to visualize changes
- Provide custom diff UIs for specific file formats
How Plugins Work
When you insert or update a file, Lix automatically invokes the matching plugin based on the file path:
File updated
↓
Plugin matched by glob pattern (*.json, *.md, etc.)
↓
Plugin.detectChanges() identifies entity changes
↓
Changes stored in database
When you read a file back:
Query entities from database
↓
Plugin.applyChanges() reconstructs file
↓
File data returned
Using Plugins
Register plugins when opening a lix:
import { openLix } from "@lix-js/sdk";
import { plugin as jsonPlugin } from "@lix-js/plugin-json";
import { plugin as markdownPlugin } from "@lix-js/plugin-md";
const lix = await openLix({
providePlugins: [jsonPlugin, markdownPlugin],
});
// Insert a JSON file - plugin automatically detected by *.json pattern
await lix.db
.insertInto("file")
.values({
path: "/config.json",
data: new TextEncoder().encode(JSON.stringify({ theme: "dark" })),
})
.execute();
The plugin is automatically matched based on the file path and the plugin's detectChangesGlob pattern.
Building Custom Plugins
Plugins implement the LixPlugin interface with two main methods:
detectChanges({ before, after, querySync })- Detects entity changes between file statesapplyChanges({ file, changes })- Reconstructs files from entity states
For implementation examples, see the official plugin source code:
- plugin-json - Simple example using JSON Pointer for entity IDs
- plugin-md - Advanced example with stable ID reconciliation using
querySync
These implementations demonstrate:
- Parsing files and detecting entity changes
- Using
querySyncto preserve stable entity IDs - Applying changes to reconstruct files
- Schema definitions for entity types
Plugin Best Practices
Use Stable Entity IDs
- Generate deterministic IDs based on content or position
- Use
querySyncto preserve existing IDs across changes - Avoid random IDs that change on every edit
Choose the Right Granularity
- Too fine: Many entities, complex queries, harder merges
- Too coarse: Large entities, less precise diffs
- Consider your use case: commenting needs fine granularity, auditing may not
Handle Edge Cases
- Empty files
- Deleted files (file.data may be undefined in applyChanges)
- Malformed content
- Unicode and special characters
Optimize Performance
- Use
querySyncsparingly - it's synchronous but has overhead - Cache parsed data when possible
- Consider incremental parsing for large files
Test Thoroughly
- Verify
detectChangesandapplyChangesare inverses - Test with various file sizes and content types
- Ensure entity IDs remain stable across edits
Multiple Plugins
Register multiple plugins to handle different file types:
import { plugin as jsonPlugin } from "@lix-js/plugin-json";
import { plugin as csvPlugin } from "@lix-js/plugin-csv";
import { plugin as markdownPlugin } from "@lix-js/plugin-md";
const lix = await openLix({
providePlugins: [jsonPlugin, csvPlugin, markdownPlugin],
});
Each file is automatically processed by the plugin whose detectChangesGlob pattern matches the file path.
Finding Plugins
Browse the plugins directory for official and community plugins, installation instructions, and file type coverage.
Next Steps
- Learn about Schemas to understand entities and schemas
- See SQL Interface to work with plugin-detected changes
- Explore the API Reference for detailed plugin interfaces