Custom Plugins
Besides the built-in plugins (Theme, Language, Storage, Global App State), Blits allows you to create your own custom plugins. Custom plugins are a great way to encapsulate reusable logic and make it available across all components in your App via the this scope.
Basic Plugin Structure
A Blits plugin can be defined in two ways: as a plugin object or as a function.
Object-style plugin
The most common approach is to export an object with a name and a plugin function:
// plugins/myPlugin.js
export default {
name: 'myPlugin',
plugin(options) {
// options is the object passed when registering the plugin
return {
greet() {
return `Hello, ${options.name || 'World'}!`
},
}
},
}Function-style plugin
Alternatively, you can export a plain function. In this case, you must provide the plugin name when registering it:
// plugins/myPlugin.js
export default function(options) {
return {
greet() {
return `Hello, ${options.name || 'World'}!`
},
}
}Registering a Custom Plugin
Custom plugins are registered in the App's index.js using Blits.Plugin(), just like the built-in plugins.
Make sure to place the Blits.Plugin() method before calling the Blits.Launch() method.
// index.js
import Blits from '@lightningjs/blits'
import App from './App.js'
// Object-style plugin
import myPlugin from './plugins/myPlugin.js'
Blits.Plugin(myPlugin, { name: 'Blits' })
// Or function-style plugin (name is required as second argument)
import myFunctionPlugin from './plugins/myFunctionPlugin.js'
Blits.Plugin(myFunctionPlugin, 'myFunctionPlugin', { name: 'Blits' })
Blits.Launch(App, 'app', {
// launch settings
})Once registered, the plugin is available on every Component's this scope, prefixed with a $ sign:
Blits.Component('MyComponent', {
hooks: {
ready() {
console.log(this.$myPlugin.greet()) // "Hello, Blits!"
},
},
})Creating Reactive State in Plugins
A common need for plugins is to hold reactive state — state that, when changed, automatically triggers template updates in any component that references it. Think of how the built-in Theme and Language plugins reactively update the UI when the theme or language changes.
Blits makes this easy by providing a this.$reactive() method on every plugin's this context. This method is pre-configured with the App's reactivity mode, so you don't need to import anything or worry about internal configuration.
this.$reactive(target)
Wraps a plain object in a reactive proxy. Changes to properties on the returned object will automatically trigger re-renders in any component that accesses them.
The $reactive method is part of the shared properties that Blits automatically applies to every plugin instance — the same mechanism that gives plugins access to this.$emit(), this.$listen(), and other framework utilities.
Example: A reactive counter plugin
// plugins/counter.js
export default {
name: 'counter',
plugin(options) {
const state = this.$reactive({
count: options.initial || 0,
})
return {
get count() {
return state.count
},
increment() {
state.count++
},
decrement() {
state.count--
},
reset() {
state.count = options.initial || 0
},
}
},
}Register the plugin:
import Blits from '@lightningjs/blits'
import counter from './plugins/counter.js'
Blits.Plugin(counter, { initial: 0 })Use it in a component — the template will automatically update when the count changes:
Blits.Component('MyComponent', {
template: `
<Element>
<Text :content="'Count: ' + $$counter.count" />
</Element>
`,
input: {
up() {
this.$counter.increment()
},
down() {
this.$counter.decrement()
},
enter() {
this.$counter.reset()
},
},
})Note that in the template, you use two dollar signs ($$counter.count). The first $ refers to the Component scope (as with any state or computed property), and the second $ is the plugin prefix.
Accessing Other Plugins
Plugins can access other registered plugins directly. After all plugins are instantiated, Blits automatically exposes each plugin instance to every other plugin. So inside a plugin's returned methods, you can use this.$otherPlugin:
export default {
name: 'analytics',
plugin(options) {
return {
trackPage(page) {
// Access the session plugin from within the analytics plugin
const user = this.$session?.loggedIn ? this.$session.user.name : 'anonymous'
console.log(`[${user}] Visited: ${page}`)
},
}
},
}Tips
- Only use
this.$reactive()when you need the state to trigger template updates. For static configuration or utility methods, a plain object is sufficient. - You can create reactive state directly inside
plugin()and use it from returned methods via closure. - Expose reactive state through getters (e.g.,
get count()) rather than exposing the raw reactive object directly. This ensures a clean API and prevents external code from accidentally bypassing reactivity. - Keep plugins focused and small. If a plugin grows too complex, consider splitting it into multiple plugins.