Skip to content

Instantly share code, notes, and snippets.

@Himujjal
Created January 14, 2026 17:36
Show Gist options
  • Select an option

  • Save Himujjal/97ce4ab99b2482bcfdb7400a2cb74ea6 to your computer and use it in GitHub Desktop.

Select an option

Save Himujjal/97ce4ab99b2482bcfdb7400a2cb74ea6 to your computer and use it in GitHub Desktop.
Yukti is a minimal version of SolidJS in one single TypeScript file without any dependencies.
/**
* Yukti Framework v1.0.0
* Minimal reactive core with proper TypeScript support
*/
// ==========================================
// 1. Core Types & Interfaces
// ==========================================
export type Accessor<T> = () => T;
export type Setter<T> = (value: T | ((prev: T) => T)) => T;
export type Signal<T> = [Accessor<T>, Setter<T>];
export type EffectFn = () => void | CleanupFn;
export type CleanupFn = () => void;
export type Component<P = {}> = (props: P) => Renderable;
export type Renderable = VNode | VNode[] | string | number | boolean | null | undefined;
interface VNodeBase {
type: string | Component<any>;
props: Record<string, any>;
children?: Renderable[];
key?: string | number;
}
interface ElementVNode extends VNodeBase {
type: string;
$$typeof: typeof ELEMENT_TYPE;
}
interface ComponentVNode extends VNodeBase {
type: Component<any>;
$$typeof: typeof COMPONENT_TYPE;
result?: Renderable;
owner?: Owner;
}
export type VNode = ElementVNode | ComponentVNode;
interface Effect {
fn: EffectFn;
dependencies: Set<Set<() => void>>;
cleanup?: CleanupFn;
execute: () => void;
}
interface Owner {
owner: Owner | null;
cleanups: Set<CleanupFn>;
mounts: Set<() => void>;
mounted: boolean;
context?: Map<symbol, unknown>;
}
interface Context<T> {
id: symbol;
defaultValue: T;
}
// ==========================================
// 2. Constants
// ==========================================
const ELEMENT_TYPE = Symbol.for('yukti.element');
const COMPONENT_TYPE = Symbol.for('yukti.component');
// ==========================================
// 3. Global State
// ==========================================
let currentOwner: Owner | null = null;
let currentEffect: Effect | null = null;
let pendingEffects = new Set<() => void>();
let isBatching = false;
// ==========================================
// 4. Reactive State Management
// ==========================================
export function createSignal<T>(initialValue: T): Signal<T> {
let value = initialValue;
const subscribers = new Set<() => void>();
const get: Accessor<T> = () => {
if (currentEffect) {
subscribers.add(currentEffect.execute);
currentEffect.dependencies.add(subscribers);
}
return value;
};
const set: Setter<T> = (newValue) => {
const nextValue = typeof newValue === 'function'
? (newValue as (prev: T) => T)(value)
: newValue;
if (!Object.is(nextValue, value)) {
value = nextValue;
if (!isBatching) {
subscribers.forEach(sub => sub());
} else {
subscribers.forEach(sub => pendingEffects.add(sub));
}
}
return value;
};
return [get, set];
}
export function createMemo<T>(fn: () => T): Accessor<T> {
const [signal, setSignal] = createSignal<T>(undefined as any);
createEffect(() => {
const value = fn();
setSignal(() => value);
});
return signal;
}
// ==========================================
// 5. Effects & Lifecycle
// ==========================================
export function createEffect(fn: EffectFn): () => void {
const effect: Effect = {
fn,
dependencies: new Set(),
cleanup: undefined,
execute: () => {
effect.dependencies.forEach(signalSubscribers => {
signalSubscribers.delete(effect.execute);
});
effect.dependencies.clear();
if (effect.cleanup) {
effect.cleanup();
effect.cleanup = undefined;
}
const prevEffect = currentEffect;
const prevOwner = currentOwner;
const effectOwner: Owner = {
owner: prevOwner,
cleanups: new Set(),
mounts: new Set(),
mounted: false
};
currentEffect = effect;
currentOwner = effectOwner;
try {
const result = fn();
if (typeof result === 'function') {
effect.cleanup = result;
onCleanup(result);
}
} finally {
currentEffect = prevEffect;
currentOwner = prevOwner;
}
}
};
effect.execute();
return () => {
effect.dependencies.forEach(signalSubscribers => {
signalSubscribers.delete(effect.execute);
});
if (effect.cleanup) effect.cleanup();
};
}
export function batch<T>(fn: () => T): T {
isBatching = true;
try {
return fn();
} finally {
isBatching = false;
flushEffects();
}
}
function flushEffects(): void {
const effects = Array.from(pendingEffects);
pendingEffects.clear();
effects.forEach(effect => effect());
}
export function onCleanup(fn: CleanupFn): void {
if (currentOwner) {
currentOwner.cleanups.add(fn);
}
}
export function onMount(fn: () => void): void {
if (currentOwner) {
if (!currentOwner.mounted) {
currentOwner.mounts.add(fn);
} else {
fn();
}
}
}
// ==========================================
// 6. Context API
// ==========================================
export function createContext<T>(defaultValue: T): Context<T> {
return {
id: Symbol('yukti.context'),
defaultValue
};
}
export function createContextProvider<T>(
context: Context<T>,
value: T,
children: Renderable | (() => Renderable)
): Renderable {
const providerOwner: Owner = {
owner: currentOwner,
cleanups: new Set(),
mounts: new Set(),
mounted: false,
context: new Map(currentOwner?.context)
};
providerOwner.context!.set(context.id, value);
const prevOwner = currentOwner;
currentOwner = providerOwner;
try {
const result = typeof children === 'function' ? children() : children;
if (!providerOwner.mounted) {
providerOwner.mounted = true;
providerOwner.mounts.forEach(cb => cb());
providerOwner.mounts.clear();
}
return result;
} finally {
currentOwner = prevOwner;
}
}
export function useContext<T>(context: Context<T>): T {
let owner = currentOwner;
while (owner) {
if (owner.context && owner.context.has(context.id)) {
return owner.context.get(context.id) as T;
}
owner = owner.owner;
}
return context.defaultValue;
}
// ==========================================
// 7. Component & JSX Factory
// ==========================================
function normalizeChildren(children: unknown[]): Renderable[] {
const result: Renderable[] = [];
for (const child of children) {
if (child == null || child === false || child === true) {
continue;
}
if (Array.isArray(child)) {
result.push(...normalizeChildren(child));
} else {
result.push(child as Renderable);
}
}
return result;
}
export function h(
type: string | Component<any>,
props?: Record<string, unknown> | null,
...children: unknown[]
): VNode {
const finalProps: Record<string, unknown> = props ? { ...props } : {};
const normalizedChildren = normalizeChildren(children);
if (normalizedChildren.length > 0) {
finalProps.children = normalizedChildren.length === 1
? normalizedChildren[0]
: normalizedChildren;
}
if (typeof type === 'function') {
const componentOwner: Owner = {
owner: currentOwner,
cleanups: new Set(),
mounts: new Set(),
mounted: false
};
const prevOwner = currentOwner;
currentOwner = componentOwner;
let result: Renderable;
try {
result = type(finalProps);
} finally {
currentOwner = prevOwner;
}
const vnode: ComponentVNode = {
$$typeof: COMPONENT_TYPE,
type,
props: finalProps,
result,
owner: componentOwner
};
return vnode;
}
const vnode: ElementVNode = {
$$typeof: ELEMENT_TYPE,
type,
props: finalProps,
children: normalizedChildren
};
return vnode;
}
// ==========================================
// 8. Error Boundary Component
// ==========================================
export interface ErrorBoundaryProps {
fallback: Component<{ error: Error }> | Renderable;
children: Renderable | (() => Renderable);
}
export function ErrorBoundary({ fallback, children }: ErrorBoundaryProps): Renderable {
const [error, setError] = createSignal<Error | null>(null);
try {
const content = typeof children === 'function' ? children() : children;
if (error()) {
return typeof fallback === 'function'
? fallback({ error: error()! })
: fallback;
}
return content;
} catch (err) {
setError(err as Error);
return typeof fallback === 'function'
? fallback({ error: err as Error })
: fallback;
}
}
// ==========================================
// 9. Core Export
// ==========================================
export const Yukti = {
// Core reactivity
createSignal,
createEffect,
createMemo,
batch,
// Lifecycle
onCleanup,
onMount,
// Context
createContext,
createContextProvider,
useContext,
// Components & JSX
h,
ErrorBoundary,
} as const;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment