Kempo UI Icon Kempo UI

Kempo UI

Kempo UI Icon
Base Components
Utils

Context

Table of Contents

k-context is a non-rendering state container inspired by React's useContext. Place it in the DOM as an ancestor of components that need shared state. Descendant components locate the nearest k-context via closest('k-context'), read and write data with its methods, and react to changes by listening for its events.

Basic Usage

Declare a <k-context> element in your markup. It is invisible and renders nothing; its display: contents style ensures its children remain in the normal document flow.

<k-context id="myContext"></k-context>

You can pre-populate the store declaratively by setting the data attribute to a valid JSON string:

<k-context id="myContext" data='{"count":0,"theme":"dark"}'></k-context>

Use set() and get() to write and read data by key:

const ctx = document.querySelector('k-context');
ctx.set('count', 0);
ctx.set('theme', 'dark');

Listening to Events

context:create fires when a new key is set for the first time. context:set fires when an existing key's value changes. context:delete fires when a key is removed. All events bubble and are composed.

const ctx = document.querySelector('k-context');
ctx.addEventListener('context:create', e => {
console.log('Created:', e.detail.key, e.detail.value);
});
ctx.addEventListener('context:set', e => {
console.log('Updated:', e.detail.key, 'from', e.detail.oldValue, 'to', e.detail.value);
});
ctx.addEventListener('context:delete', e => {
console.log('Deleted:', e.detail.key, 'was', e.detail.value);
});

Using in Components

Child components locate the nearest ancestor context using closest('k-context'). This follows the same scoping pattern used by all Kempo UI parent–child relationships.

// Inside a child component, find the nearest ancestor context:
const ctx = this.closest('k-context');
const count = ctx?.get('count');

To react to context changes from inside a shadow-DOM component, listen on the context element directly — events bubble and are composed: true so they also cross shadow boundaries.

Persistence

Set a persistent-id to automatically save the store to localStorage and restore it on the next page load — the same convention used by Split, Accordion, Tabs and Aside. The data is reloaded in connectedCallback (before descendants read it) and re-saved on every set(), delete() and clear().

<k-context persistent-id="app-settings"></k-context>

Because the entire store is JSON-serialized into a single value, keep persisted contexts to small, serializable state (settings, selections, ids) — not large or non-JSON data. Scope what you persist by giving config its own persistent-id context and leaving transient state in a separate, non-persistent k-context.

Interactive Demo

<k-context data='{"count":0}'>
<button
onclick="const ctx=this.closest('k-context'); ctx.set('count', (ctx.get('count') || 0) - 1)"
>
-</button>
<count-view></count-view>
<button
onclick="const ctx=this.closest('k-context'); ctx.set('count', (ctx.get('count') || 0) + 1)"
>
+</button>
<button
onclick="this.closest('k-context').clear()"
>
Clear</button>
</k-context>
<script type="module">
import '../src/components/Context.js';
import ShadowComponent from '../src/components/ShadowComponent.js';
import { html } from '../src/lit-all.min.js';
class CountView extends ShadowComponent {
connectedCallback() {
super.connectedCallback();
const ctx = this.closest('k-context');
ctx?.addEventListener('context:create', () => this.requestUpdate());
ctx?.addEventListener('context:set', () => this.requestUpdate());
ctx?.addEventListener('context:delete', () => this.requestUpdate());
}
render() {
const count = this.closest('k-context')?.get('count');
return count !== undefined ? html`${count}` : html`<code>undefined</code>`;
}
}
customElements.define('count-view', CountView);
</script>

JavaScript Reference

Constructor

k-context extends LightComponent. It renders nothing and sets display: contents on itself so its children remain in the normal document flow. It accepts an optional data attribute containing a JSON string to pre-populate the context state.

Attributes

Attribute Type Description
data String (JSON) A JSON string representing the initial store state. Reflects back to the attribute as state changes via the component's methods. Defaults to '{}'.
persistent-id String When set, the store is auto-saved to localStorage (key context-persistent-id-<id>) on every change and restored on connect. Defaults to null (no persistence).

Methods

Method Description
set(key, value) Creates or updates a key. Fires context:create if the key is new, context:set if it already existed.
get(key) Returns the stored value for key, or undefined if not set.
has(key) Returns true if key exists in the store.
delete(key) Removes a key and fires context:delete. No-op if the key does not exist.
clear() Calls delete() on every key, firing context:delete for each.
getData() Returns a shallow copy of the entire data store as a plain object.

Events

All events bubble and are composed: true, so they cross shadow DOM boundaries.

Event Fired when detail shape
context:create A new key is set for the first time { key, value }
context:set An existing key's value is changed { key, value, oldValue }
context:delete A key is removed via delete() or clear() { key, value }