root

Creates a new top-level owner scope. The callback receives a dispose function that tears down everything created inside. Reactive work that outlives a component (long-lived subscriptions, imperative rendering, stores instantiated at module load) belongs in a root. render creates one for you internally; call root directly when there's no render tree.

Arguments

name type description
fn (dispose) => T runs once, immediately. Receives the disposer for the root.
options? EffectOptions optional owner options assigned onto the root.

Returns: the return value of fn.

Examples

Detached scope with manual dispose

root runs fn in a new owner that is not attached to any parent — cleanup registrations stick around until you call the disposer fn received. Use it for reactive code outside a component tree (workers, modal managers, background timers).

import { cleanup, effect, root, signal } from 'pota'

const dispose = root(dispose => {
	const ticks = signal(0)

	effect(() => {
		document.title = `ticks: ${ticks.read()}`
	})

	const id = setInterval(() => ticks.update(n => n + 1), 1000)
	cleanup(() => clearInterval(id))

	return dispose
})

// later, e.g. on hot-reload or page teardown:
// dispose()

Module-level reactive store

Wrapping a module's reactive setup in root gives those primitives their own owner — they don't get cleaned up when a component unmounts, and any cleanups they register live for the whole page.

import { effect, root, signal } from 'pota'

export const session = root(() => {
	const user = signal(null)

	effect(() => {
		if (user.read()) {
			document.documentElement.classList.add('logged-in')
		} else {
			document.documentElement.classList.remove('logged-in')
		}
	})

	return {
		user: user.read,
		login: u => user.write(u),
		logout: () => user.write(null),
	}
})