context

Context carries values through the reactive scope so deeply nested components can read them without prop drilling. A single function acts as both the provider and the reader.

context(defaultValue?) returns a useContext function. Call it with no argument to read the current value (or the default if nothing was provided). Call it with a value and a fn to push that value for the duration of fn — children rendered inside read the new value; everything outside keeps the previous one. The new value replaces the inherited one entirely; to inherit-and-override, build the merged object yourself before providing it.

A Provider JSX component is attached for use in the tree, plus a walk helper that climbs the parent chain of a context value.

Arguments

name type description
defaultValue T value returned by reads when no provider is in scope. Optional.

Returns: useContext(newValue?, fn?) — a function that reads when called bare and provides for the duration of fn when called with (newValue, fn), returning fn's result. It carries:

API shape

// create (defaultValue is optional)
const useCtx = context('default value')

// read
useCtx()

// provide, scoped to fn
useCtx('new value', fn)

Examples

Theme provider

context(defaultValue) returns a useContext function with a Provider JSX component attached. Wrap a subtree in <Theme.Provider value={…}/> and any descendant call to Theme() returns that value; with no provider, Theme() returns the default. Providers nest — inner ones override outer ones for their subtree.

import { context, render } from 'pota'

const Theme = context('light')

function Label() {
	return <p>theme is {Theme()}</p>
}

function App() {
	return (
		<div>
			<Label />
			<Theme.Provider value="dark">
				<Label />
				<Theme.Provider value="contrast">
					<Label />
				</Theme.Provider>
			</Theme.Provider>
		</div>
	)
}

render(App)

Functional override

Calling the context function as Theme(newValue, fn) runs fn with the override applied and returns its result; outside fn, the previous value is restored. Useful when you need the context override outside the JSX tree — computing a derived value, inside an effect, and so on.

import { context, render, signal } from 'pota'

const Theme = context('light')

const log = signal('')
log.write(`Theme(): ${Theme()}`) // 'light'

const result = Theme('dark', () => {
	return Theme('contrast', () => Theme())
})

log.update(s => s + `\nresult: ${String(result)}`) // 'contrast'
log.update(s => s + `\nTheme(): ${Theme()}`) // 'light' — restored

function App() {
	return <p>{log.read}</p>
}

render(App)

Reactive context value

Putting a signal into the context value gives descendants both a reactive read and a write channel. The provider owns the signal once; any nested component swaps the theme by calling the context's set channel. Note the reader is passed as theme.read (the reader function) so the read stays reactive.

import { context, render, signal } from 'pota'

type ThemeContext = {
	value: () => string
	set: (next: string) => void
}

const Theme = context<ThemeContext>({
	value: () => 'light',
	set: next => {},
})

function Toolbar() {
	const t = Theme()
	return (
		<div>
			<p>current: {t.value}</p>
			<button on:click={() => t.set('light')}>light</button>
			<button on:click={() => t.set('dark')}>dark</button>
		</div>
	)
}

function App() {
	const theme = signal('light')
	return (
		<Theme.Provider
			value={{
				value: theme.read,
				set: v => theme.write(v),
			}}
		>
			<Toolbar />
		</Theme.Provider>
	)
}

render(App)