Registry / web-framework / unctx
library2.5.0jsnpmunverified

Unctx is a lightweight JavaScript library that implements the Composition API pattern, popularized by Vue.js, in vanilla JS. It empowers developers to structure complex logic into reusable functions and manage context-like state within their libraries and applications. The current stable version is 2.5.0, with consistent releases indicating active development. A key differentiator is its robust handling of asynchronous contexts, offering support for Node.js's native `AsyncLocalStorage` for persistence across async operations, alongside a build-time transformation for environments lacking native support. It also features a global namespacing mechanism via `globalThis` to mitigate context conflicts across multiple library instances or versions, emphasizing unique and verbose keys for stability.

npm install unctx
INSTALL
IMPORT
SIG · UNCTX
U
unctx
web-frameworkjavascriptv2.5.0
Install
Import
Disk
Pass rate
0/ 6
Env Coverage0 / 6
glibc
1822
musl
1822
Install & Compatibility
Where this runs
tested against v? · npm install
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
musl
node 18226 runs
build_error
glibc
node 18226 runs
build_error
Code
Verified usage

Verified import paths — ran on the pinned version, not inferred.

createContext
import { createContext } from 'unctx'
const { createContext } = require('unctx')
Primary function to create a new, isolated context. Unctx is an ESM-first package.
useContext
import { useContext } from 'unctx'
const useContext = require('unctx').useContext
Used for globally namespaced contexts, accepting a string key to retrieve or create a context.
unctxPlugin
import { unctxPlugin } from 'unctx/plugin'
import { unctxPlugin } from 'unctx'
The build-time transformation plugin is imported from a subpath and requires a bundler like Rollup, Vite, or Webpack.
AsyncLocalStorage
import { AsyncLocalStorage } from 'node:async_hooks'
import AsyncLocalStorage from 'async_hooks'
Used when enabling native async context. Requires Node.js (or a polyfill/runtime with support like Cloudflare Workers).

This quickstart demonstrates creating a context, defining a composable, using `ctx.call` to activate the context, and showing how `AsyncLocalStorage` (if configured) preserves context across `await` statements. It also highlights the error when no context is active.

import { createContext } from 'unctx'; import { AsyncLocalStorage } from 'node:async_hooks'; // Only needed for native async context in Node.js // Create a context, optionally enabling native async context const ctx = createContext({ asyncContext: true, // Enable native async context if available AsyncLocalStorage: typeof AsyncLocalStorage !== 'undefined' ? AsyncLocalStorage : undefined }); // Define a composable that uses this context export const useCounter = () => { const state = ctx.use(); if (!state) { throw new Error('useCounter must be called within an active context.'); } return { increment: () => state.count++, decrement: () => state.count--, getCount: () => state.count, setCount: (value) => { state.count = value; } }; }; async function runExample() { console.log('--- Without active context ---'); try { useCounter().getCount(); // This will throw if no context is active } catch (e) { console.error(e.message); } console.log('\n--- Running with context ---'); await ctx.call({ count: 0 // Initial context state }, async () => { const counter = useCounter(); console.log('Initial count:', counter.getCount()); // Should be 0 counter.increment(); console.log('After increment:', counter.getCount()); // Should be 1 await new Promise(resolve => setTimeout(resolve, 50)); // With asyncContext: true, the context should persist across await console.log('After await, count:', counter.getCount()); // Should still be 1 counter.increment(); console.log('Final count:', counter.getCount()); // Should be 2 }); console.log('\n--- After context call ends ---'); try { useCounter().getCount(); } catch (e) { console.error(e.message); } } runExample();
Debug
Known issues
breakingUnctx v2.4.0 updated its `unplugin` dependency to v2. Users utilizing the `unctx/plugin` for build-time async context transformation might experience issues or need to update their `unplugin` setup to ensure compatibility.
fix
Review your `unplugin` configuration and update to `unplugin` v2 if necessary. Consult the `unplugin` documentation for migration steps.
affects: >=2.4.0
gotchaCalling `ctx.use()` (or `useAwesome()` in the example) when no context has been set via `ctx.call()` will throw an error.
fix
Always ensure `ctx.use()` is called within a `ctx.call()` scope, or use `ctx.tryUse()` if a nullable context is acceptable for tolerant usages.
affects: >=2.0.0
gotchaWithout enabling `asyncContext: true` and providing `AsyncLocalStorage` (or using the build-time transform), context set by `ctx.call()` will be lost across asynchronous operations like `await` or `setTimeout`.
fix
For Node.js, enable native async context by passing `{ asyncContext: true, AsyncLocalStorage }` to `createContext` and `import { AsyncLocalStorage } from 'node:async_hooks'`. For other environments, use the `unctx/plugin` for build-time async transformation.
affects: <2.3.0 (before native async context support), or >=2.3.0 without explicit `asyncContext: true` configuration
gotchaWhen using `useContext` or `getContext` with namespaces, always provide a verbose and unique string key (e.g., your npm package name) to avoid conflicts within `globalThis` if multiple libraries use `unctx`.
fix
Adopt a clear naming convention for your context keys, ideally derived from your package's unique identifier to minimize collision risk.
affects: >=1.0.0
deprecatedVersion 2.0.0 introduced strict `ctx.use()` behavior, meaning it now throws errors if no context is found. The `ctx.tryUse()` method was introduced as a non-throwing alternative.
fix
Migrate usages of `ctx.use()` that previously might have implicitly handled `undefined` to `ctx.tryUse()`, or ensure `ctx.use()` is only called when a context is guaranteed to be active.
affects: <2.0.0
Errors
Common errors & fixes
Error: [unctx] No active context
The `ctx.use()` method was called outside of an active context created by `ctx.call()` or a namespaced context provided by `useContext`/`getContext`.
fix
Wrap the code that calls `ctx.use()` within a `ctx.call()` block, or ensure a namespaced context has been established using `useContext` or `getContext` and populated with `ctx.call()`.
Context value is undefined (or null) after await/setTimeout
The context was lost across an asynchronous boundary because native async context was not enabled or the build-time transform was not applied.
fix
For Node.js environments, enable native async context by passing `{ asyncContext: true, AsyncLocalStorage }` to `createContext` and importing `AsyncLocalStorage` from `node:async_hooks`. For environments without native `AsyncLocalStorage`, configure and use the `unctx/plugin` within your bundler (e.g., Rollup, Vite, Webpack) to enable build-time async transformation.
Upgrade
Version history
2.5.0latest on npm
Audit
Dependencies
node:async_hooksoptionalRequired for native async context support in Node.js environments via `AsyncLocalStorage`.
unpluginoptionalRequired for the build-time async context transformation plugin. Users of the plugin need to ensure compatibility.
Agent activity
7 hits · last 30 days
node
6
OpenAI (training)
1
Resources