Appearance
Are you an LLM? You can read better optimized documentation at /docs/custom-widgets/v2/core-concepts.md for this page in Markdown format
Widget Runtime
Understand how custom widgets are loaded, initialized, and managed by the platform. This page covers the runtime model that applies to all widgets regardless of framework — React, Vue, vanilla JavaScript, or anything else.
How Widgets Run
Every widget runs inside a Shadow DOM that the platform creates for you. The Shadow DOM isolates your widget's styles and markup from the host page. At runtime, the platform also creates an SDK instance that gives your code access to props, design tokens, events, and the shadow root where your widget renders.
The init(sdk) Contract
Every widget must export an init function (or a default export). The platform calls this function with an SDK instance after the widget connects to the DOM:
javascript
export async function init(sdk) {
await sdk.whenReady()
// Mount your UI into sdk.getContainer()
}
// OR default export
export default async function (sdk) {
await sdk.whenReady()
// Mount your UI into sdk.getContainer()
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Your widget is an ES module. The platform loads it via a <script type="module"> tag inside the widget's shadow DOM.
Widget Lifecycle
Initialization
When a widget appears on a page, the platform runs through this sequence:
Once alive, the widget receives propsChanged, designTokensChanged, and custom events until it is removed.
Teardown
When the widget is removed from the page, the SDK emits a destroy event. Use this to clean up your UI framework, cancel network requests, clear timers, and remove event listeners.
Example cleanup:
javascript
export function init(sdk) {
const interval = setInterval(() => fetchData(), 30000)
sdk.on('destroy', () => {
clearInterval(interval)
})
}1
2
3
4
5
6
7
2
3
4
5
6
7
Props
Props come from the widget's configuration (set via the No-Code Builder or Widget Definition Reference). Read them with getProps() and listen for changes with the propsChanged event:
javascript
const props = sdk.getProps()
console.log(props.title)
sdk.on('propsChanged', (newProps) => {
console.log('Config updated:', newProps)
})1
2
3
4
5
6
2
3
4
5
6
Design Tokens
Design tokens are CSS custom properties that reflect the community's branding (colors, fonts, etc.). They are injected into your shadow DOM automatically, so you can use them in CSS:
css
h1 {
color: var(--config--main-color-brand, #2563eb);
}
.card {
background: var(--config--main-color-background, #ffffff);
border-color: var(--config--main-color-border, #e5e7eb);
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Always provide fallback values (the second argument to var()) so your widget renders correctly outside a community context.
You can also read tokens programmatically with getDesignTokens() and listen for changes with the designTokensChanged event:
javascript
const tokens = sdk.getDesignTokens()
console.log(tokens['config--main-color-brand'])
sdk.on('designTokensChanged', (newTokens) => {
console.log('Branding updated:', newTokens)
})1
2
3
4
5
6
2
3
4
5
6
Custom Events
Widgets can emit and listen for custom events to communicate with the platform or other widgets:
javascript
sdk.emit('taskCompleted', { taskId: 42 })
const unsubscribe = sdk.on('taskCompleted', (data) => {
console.log('Task completed:', data)
})1
2
3
4
5
2
3
4
5
The on() method returns an unsubscribe function — call it when you no longer need the listener.
Best Practices
Shadow DOM awareness
- Mount framework apps with
sdk.getContainer()— pass it directly tocreateRoot()(React) orcreateApp().mount()(Vue). For finer control, usesdk.$('#root')to target a specific element inside the shadow root. - Query elements with
sdk.$()andsdk.$$()for scoped shadow root access. These are shorthands forshadowRoot.querySelector()andshadowRoot.querySelectorAll(). - Styles are scoped to the shadow DOM automatically — use this to your advantage.
Styling
- Prefer CSS custom properties (
var(--config--...)) for branding, so your widget adapts to the community's theme. - Use the
:hostselector to style the widget container itself. - Always provide fallback values for design tokens:
var(--config--main-color-brand, #2563eb).
Bundle size
- Tree-shake aggressively. Only import what you need.
- Lazy-load heavy dependencies with dynamic
import().
Cleanup
- Always listen for
destroyand tear down your UI. Failing to do so leaks framework state and event listeners. - Unsubscribe from SDK events when they are no longer needed (the
on()return value is the unsubscribe function). - Clear timers and cancel requests in your destroy handler.
Next Steps
- Widget SDK Reference — Properties, methods, and events on the SDK instance
- Using React — Build widgets with React
- Widget Definition Reference — Define your widget in
extensions_registry.json - Widget Configuration — Let editors customize your widget via a form in the No-Code Builder
- Common Issues — Common issues and solutions
- React widget in the template repository — Complete example showing
whenReady(),getContainer(),getProps(),propsChanged, anddestroylifecycle events

