diff --git a/packages/ctablex-core/README.md b/packages/ctablex-core/README.md
new file mode 100644
index 0000000..cf637b9
--- /dev/null
+++ b/packages/ctablex-core/README.md
@@ -0,0 +1,219 @@
+# @ctablex/core
+
+Core building blocks for composable, context-based React components using the **micro-context pattern**.
+
+## What is Micro-Context?
+
+**Micro-context** is a pattern for passing data through localized React Context instead of props. Unlike traditional context patterns that span entire applications, micro-context creates small, scoped providers within component subtrees for fine-grained data flow.
+
+This enables:
+
+- **Reusable components** that work anywhere without knowing the data source
+- **Flexible composition** of data transformers and renderers
+- **No prop drilling** through intermediate components
+- **Immutable children** for better performance optimization
+
+Read more: [Micro-Context Pattern](./docs/MICRO-CONTEXT.md)
+
+## Installation
+
+```bash
+npm install @ctablex/core
+```
+
+## Quick Start
+
+```tsx
+import { ContentProvider, FieldContent, DefaultContent } from '@ctablex/core';
+
+type User = {
+ name: string;
+ email: string;
+};
+
+const user: User = {
+ name: 'Alice',
+ email: 'alice@example.com',
+};
+
+// Provide data via context
+
+ {/* Access fields without props */}
+
+
+
+;
+// Renders: Alice
+```
+
+## Core Concepts
+
+### ContentProvider & useContent
+
+The foundation of micro-context:
+
+```tsx
+import { ContentProvider, useContent } from '@ctablex/core';
+
+// Provide data
+
+
+;
+
+// Consume data
+function MyComponent() {
+ const data = useContent();
+ return
{data}
;
+}
+```
+
+Read more: [ContentContext - ContentProvider & useContent](./docs/ContentContext.md)
+
+### Content Components
+
+Transform and access data through context:
+
+```tsx
+import {
+ ContentValue,
+ FieldContent,
+ ArrayContent,
+ ObjectContent,
+ NullableContent,
+ DefaultContent,
+ KeyContent,
+} from '@ctablex/core';
+
+// Access nested paths
+
+
+
+
+// Access object fields
+
+
+
+
+// Iterate arrays
+
+
+
+
+
+
+
+
+// Iterate objects
+
+
+ :
+
+
+
+// Handle null/undefined
+
+
+
+
+
+```
+
+Read more: [Contents](./docs/Contents.md), [ArrayContent](./docs/ArrayContent.md), [ObjectContent](./docs/ObjectContent.md)
+
+### Type-Safe Accessors
+
+Extract values with strong TypeScript support:
+
+```tsx
+import { access } from '@ctablex/core';
+
+type User = {
+ profile: {
+ name: string;
+ };
+};
+
+const user: User = { profile: { name: 'Bob' } };
+
+// Path accessor with autocomplete
+const name = access(user, 'profile.name'); // ✓ Type-safe + autocomplete
+
+// Function accessor
+const value = access(user, (u) => u.profile.name); // ✓ Type inference + auto type safety
+```
+
+Read more: [Accessors](./docs/Accessors.md)
+
+## Key Features
+
+### Reusable Renderers
+
+Components work anywhere without knowing the data source:
+
+```tsx
+function PriceDisplay() {
+ const price = useContent();
+ return ${price.toFixed(2)};
+}
+
+// Works in any context that provides a number
+
+
+;
+```
+
+### Default Children and Open for Customization
+
+Components provide sensible defaults while remaining customizable:
+
+```tsx
+// Simple - uses DefaultContent
+
+
+// Custom rendering
+
+
+
+```
+
+### Performance Optimization
+
+Immutable children enable powerful memoization:
+
+```tsx
+const content = (
+
+
+
+
+
+);
+
+function ProductList() {
+ return content; // Same reference every render
+}
+```
+
+## Type Safety Limitations
+
+⚠️ Micro-context provides weak type safety. Generic types must be manually specified and cannot be validated across context boundaries. See [MICRO-CONTEXT.md - Weak Type Safety](./docs/MICRO-CONTEXT.md#weak-type-safety) for details.
+
+## Documentation
+
+For detailed documentation, common patterns, and pitfalls, see:
+
+- **[Documentation Guide](./docs/README.md)** - Start here for a complete overview
+- **[Micro-Context Pattern](./docs/MICRO-CONTEXT.md)** - Understand the core concept
+- **[ContentContext](./docs/ContentContext.md)** - ContentProvider, useContent, ContentContext
+- **[Contents](./docs/Contents.md)** - ContentValue, FieldContent, NullableContent, DefaultContent
+- **[ArrayContent](./docs/ArrayContent.md)** - Array iteration components
+- **[ObjectContent](./docs/ObjectContent.md)** - Object iteration components
+- **[Accessors](./docs/Accessors.md)** - Type-safe value extraction
+
+## License
+
+MIT
+
+## Related Packages
+
+- **[@ctablex/table](https://www.npmjs.com/package/@ctablex/table)** - Composable table components built on @ctablex/core
diff --git a/packages/ctablex-core/docs/Accessors.md b/packages/ctablex-core/docs/Accessors.md
new file mode 100644
index 0000000..1466ac5
--- /dev/null
+++ b/packages/ctablex-core/docs/Accessors.md
@@ -0,0 +1,550 @@
+# Accessors
+
+Accessors are functions that extract values from data structures with **strong TypeScript support**. Unlike the weak type safety of generic context parameters, accessors provide **autocomplete and compile-time error detection**.
+
+**Note:** Most of the time, users don't directly interact with accessors. These types are used internally by the library (e.g., in `ContentValue` or `getKey` props).
+
+## Overview
+
+The accessor system provides three types of accessors:
+
+- **Path Accessors** - String-based paths like `"user.address.city"`
+- **Function Accessors** - Custom extraction functions like `(user) => user.fullName`
+- **Unified Accessors** - Accept either path strings, functions, `undefined`, or `null`
+
+## Path Accessors
+
+### accessByPath
+
+Accesses nested properties using a dot-separated string path with **full type safety and autocomplete**.
+
+```tsx
+function accessByPath>(
+ t: T,
+ path: K,
+): PathAccessorValue;
+```
+
+#### Example
+
+```tsx
+import { accessByPath } from '@ctablex/core';
+
+type User = {
+ name: string;
+ address: {
+ city: string;
+ zip: number;
+ };
+};
+
+const user: User = {
+ name: 'John',
+ address: { city: 'NYC', zip: 10001 },
+};
+
+// ✓ Type-safe with autocomplete
+const city = accessByPath(user, 'address.city'); // string
+const zip = accessByPath(user, 'address.zip'); // number
+
+// ✓ Compile-time error for invalid paths
+const invalid = accessByPath(user, 'address.country'); // ✗ Error!
+const typo = accessByPath(user, 'addres.city'); // ✗ Error!
+```
+
+#### Type Safety
+
+- **Autocomplete** - IDE suggests valid paths: `"name"`, `"address"`, `"address.city"`, `"address.zip"`
+- **Compile-time errors** - Invalid paths are caught during development
+- **Return type inference** - TypeScript knows `accessByPath(user, 'address.city')` returns `string`
+
+### accessByPathTo
+
+Like `accessByPath`, but constrains paths to those that return a specific type.
+
+```tsx
+function accessByPathTo>(
+ t: T,
+ path: K,
+): R & PathAccessorValue;
+```
+
+#### Example
+
+```tsx
+type Product = {
+ name: string;
+ price: number;
+ inStock: boolean;
+ metadata: {
+ weight: number;
+ sku: string;
+ };
+};
+
+const product: Product = {
+ name: 'Widget',
+ price: 99.99,
+ inStock: true,
+ metadata: { weight: 1.5, sku: 'WDG-001' },
+};
+
+// ✓ Explicit type arguments (R, T)
+const price1 = accessByPathTo(product, 'price'); // ✓
+const weight1 = accessByPathTo(product, 'metadata.weight'); // ✓
+
+// ✓ Type inference from variable annotation
+const price2: number = accessByPathTo(product, 'price'); // ✓
+const weight2: number = accessByPathTo(product, 'metadata.weight'); // ✓
+
+// ✗ Compile error - these paths don't return numbers
+const name: number = accessByPathTo(product, 'name'); // ✗ Error! 'name' is not a number path
+const stock: number = accessByPathTo(product, 'inStock'); // ✗ Error! 'inStock' is not a number path
+
+// ✗ Type mismatch - path returns string, not number
+const sku: number = accessByPath(product, 'metadata.sku'); // ✗ Error! Type 'string' is not assignable to 'number'
+```
+
+**Use case:** Ensuring extracted values match an expected type, useful for components that require specific value types.
+
+### PathAccessor Type
+
+The string literal type representing valid paths through an object structure.
+
+```tsx
+type PathAccessor = /* ... */;
+```
+
+Supports:
+
+- Object properties: `"user"`, `"address"`
+- Nested paths: `"user.address.city"` (up to 5 levels deep)
+- Array indices for tuples: `tuple.0`, `tuple.1`
+
+### PathAccessorValue Type
+
+The type of the value at a given path in an object.
+
+```tsx
+type PathAccessorValue = /* ... */;
+```
+
+Computes the type of the value accessed by a path. Used internally by `accessByPath` to infer return types.
+
+#### Example
+
+```tsx
+type User = {
+ name: string;
+ address: {
+ city: string;
+ zip: number;
+ };
+};
+
+type Name = PathAccessorValue; // string
+type City = PathAccessorValue; // string
+type Zip = PathAccessorValue; // number
+```
+
+### PathAccessorTo Type
+
+The string literal type representing paths that return a specific type.
+
+```tsx
+type PathAccessorTo = /* ... */;
+```
+
+Filters `PathAccessor` to only include paths where `PathAccessorValue` extends `R`.
+
+#### Example
+
+```tsx
+type Product = {
+ name: string;
+ price: number;
+ metadata: {
+ weight: number;
+ sku: string;
+ };
+};
+
+// Only paths that return numbers
+type NumberPaths = PathAccessorTo;
+// Result: "price" | "metadata.weight"
+
+// Only paths that return strings
+type StringPaths = PathAccessorTo;
+// Result: "name" | "metadata.sku"
+```
+
+## Function Accessors
+
+### accessByFn
+
+Accesses values using a custom function with type inference.
+
+```tsx
+function accessByFn>(
+ obj: T,
+ fn: F,
+): FnAccessorValue;
+```
+
+**Note:** This function is rarely used directly. Use the unified `access` function instead.
+
+#### Example
+
+```tsx
+type User = {
+ firstName: string;
+ lastName: string;
+};
+
+const user: User = {
+ firstName: 'John',
+ lastName: 'Doe',
+};
+
+// Custom extraction
+const fullName = accessByFn(user, (u) => `${u.firstName} ${u.lastName}`);
+// Returns: "John Doe"
+
+// Complex logic
+const isJohn = accessByFn(user, (u) => u.firstName === 'John');
+// Returns: true
+```
+
+### FnAccessor Type
+
+A function that extracts a value from an object.
+
+```tsx
+type FnAccessor = (t: T) => R;
+```
+
+### FnAccessorValue Type
+
+The return type of a function accessor.
+
+```tsx
+type FnAccessorValue> = /* ... */;
+```
+
+Infers the return type of a function accessor. Used internally by `accessByFn` to determine return types.
+
+#### Example
+
+```tsx
+type User = {
+ firstName: string;
+ lastName: string;
+};
+
+type FullNameFn = (u: User) => string;
+type FullNameValue = FnAccessorValue; // string
+
+type IsAdultFn = (u: User) => boolean;
+type IsAdultValue = FnAccessorValue; // boolean
+```
+
+## Unified Accessors
+
+### access
+
+The main accessor function that accepts **path strings, functions, `undefined`, or `null`**.
+
+```tsx
+function access>(t: T, a: A): AccessorValue;
+```
+
+#### Behavior
+
+- **`undefined`** - Returns the input value unchanged
+- **`null`** - Returns `null`
+- **String** - Uses `accessByPath`
+- **Function** - Calls the function with the input value
+
+#### Example
+
+```tsx
+import { access } from '@ctablex/core';
+
+type User = {
+ name: string;
+ age: number;
+};
+
+const user: User = { name: 'Alice', age: 30 };
+
+// Path accessor
+access(user, 'name'); // "Alice"
+
+// Function accessor
+access(user, (u) => u.age > 18); // true
+
+// Undefined - returns input
+access(user, undefined); // { name: 'Alice', age: 30 }
+
+// Null - returns null
+access(user, null); // null
+```
+
+### accessTo
+
+Like `access`, but constrains to accessors that return a specific type.
+
+```tsx
+function accessTo>(
+ t: T,
+ a: A,
+): R & AccessorValue;
+```
+
+#### Example
+
+```tsx
+type Product = {
+ name: string;
+ price: number;
+ tags: string[];
+};
+
+const product: Product = {
+ name: 'Widget',
+ price: 99.99,
+ tags: ['electronics', 'gadget'],
+};
+
+// ✓ Explicit type arguments (R, T)
+const price1 = accessTo(product, 'price'); // 99.99
+const doubled1 = accessTo(product, (p) => p.price * 2); // 199.98
+
+// ✓ Type inference from variable annotation
+const price2: number = accessTo(product, 'price'); // 99.99
+const doubled2: number = accessTo(product, (p) => p.price * 2); // 199.98
+const length: number = accessTo(product, (p) => p.tags.length); // 2
+
+// ✗ Compile error - accessor doesn't return number
+const name1: number = accessTo(product, 'name'); // ✗ Error! 'name' is not a number path
+const tags: number = accessTo(product, (p) => p.tags); // ✗ Error! Returns string[], not number
+
+// ✗ Type mismatch - path returns string, not number
+const name2: number = access(product, 'name'); // ✗ Error! Type 'string' is not assignable to 'number'
+```
+
+### Accessor Type
+
+Union type accepting all accessor types.
+
+```tsx
+type Accessor = undefined | null | PathAccessor | FnAccessor;
+```
+
+### AccessorValue Type
+
+The type of the value returned by an accessor.
+
+```tsx
+type AccessorValue> = A extends undefined
+ ? T
+ : A extends null
+ ? null
+ : A extends PathAccessor
+ ? PathAccessorValue
+ : A extends FnAccessor
+ ? FnAccessorValue
+ : never;
+```
+
+Computes the return type of the `access` function based on the accessor type:
+
+- `undefined` → returns `T` (the input)
+- `null` → returns `null`
+- Path string → returns `PathAccessorValue`
+- Function → returns `FnAccessorValue`
+
+#### Example
+
+```tsx
+type User = {
+ name: string;
+ age: number;
+};
+
+type Value1 = AccessorValue; // User
+type Value2 = AccessorValue; // null
+type Value3 = AccessorValue; // string
+type Value4 = AccessorValue boolean>; // boolean
+```
+
+### AccessorTo Type
+
+Union type accepting accessors that return a specific type.
+
+```tsx
+type AccessorTo =
+ | undefined
+ | null
+ | PathAccessorTo
+ | FnAccessor;
+```
+
+Like `Accessor`, but constrains path accessors to those that return type `R`, and function accessors to those with return type `R`.
+
+#### Example
+
+```tsx
+type Product = {
+ name: string;
+ price: number;
+ metadata: {
+ weight: number;
+ sku: string;
+ };
+};
+
+// Accepts any accessor that returns a number
+type NumberAccessor = AccessorTo;
+
+// Valid number accessors:
+const accessor1: NumberAccessor = 'price'; // ✓ Path to number
+const accessor2: NumberAccessor = 'metadata.weight'; // ✓ Path to number
+const accessor3: NumberAccessor = (p) => p.price * 2; // ✓ Function returning number
+const accessor4: NumberAccessor = undefined; // ✓ Returns Product (any)
+const accessor5: NumberAccessor = null; // ✓ Returns null
+
+// Invalid - don't return numbers:
+const accessor6: NumberAccessor = 'name'; // ✗ Error! Path to string
+const accessor7: NumberAccessor = (p) => p.name; // ✗ Error! Function returns string
+```
+
+## Type Safety Advantages
+
+Accessors have **strong type safety** compared to generic context parameters:
+
+### Path Accessors
+
+```tsx
+type User = {
+ address: {
+ city: string;
+ };
+};
+
+// ✓ Autocomplete suggests: "address", "address.city"
+accessByPath(user, 'address.city');
+
+// ✗ Compile-time error
+accessByPath(user, 'address.country'); // Property 'country' does not exist
+accessByPath(user, 'addres.city'); // Property 'addres' does not exist
+```
+
+### Function Accessors
+
+```tsx
+// ✓ Full type inference
+access(user, (u) => u.address.city); // TypeScript knows return type is string
+
+// ✓ Autocomplete works inside function
+access(user, (u) => u./* autocomplete shows: address */);
+```
+
+### Compared to Generic Context
+
+Accessors provide validation **based on the provided generic type**, while context generics themselves are not validated:
+
+```tsx
+type User = {
+ address: {
+ city: string;
+ };
+};
+
+// ✗ Weak type safety - generic type itself is not validated
+const city = useContent(); // TypeScript cannot verify User matches actual context value
+
+// ✓ Strong type safety - accessor IS validated based on generic type
+ accessor="address.city"> {/* ✓ TypeScript validates "address.city" exists on User */}
+
+
+
+ accessor="address.country"> {/* ✗ Error! "country" doesn't exist on User */}
+
+
+```
+
+**Key difference:**
+
+- Generic type `` on context is not validated by TypeScript (you could pass wrong type)
+- But **given** that generic type, the `accessor` prop is fully validated with autocomplete
+- This provides partial type safety: accessors are validated, but the generic type itself is not
+
+## Limitations
+
+### Path Depth
+
+Path accessors support up to **5 levels of nesting**:
+
+```tsx
+// ✓ Supported
+'a.b.c.d.e';
+
+// ✗ Not supported
+'a.b.c.d.e.f'; // Too deep
+```
+
+### Arrays
+
+Path accessors work with **tuple types** but not generic arrays:
+
+```tsx
+type Tuple = [string, number];
+const tuple: Tuple = ['hello', 42];
+
+// ✓ Works with tuples
+accessByPath(tuple, '0'); // "hello"
+accessByPath(tuple, '1'); // 42
+
+// ✗ Generic arrays require function accessors
+type StringArray = string[];
+const arr: StringArray = ['a', 'b', 'c'];
+access(arr, (a) => a[0]); // Use function instead
+```
+
+## Usage with Content Components
+
+Accessors are commonly used with `ContentValue` and other content components:
+
+```tsx
+import { ContentValue, FieldContent } from '@ctablex/core';
+
+type User = {
+ profile: {
+ name: string;
+ age: number;
+ };
+};
+
+// Path accessor
+ accessor="profile.name">
+
+
+
+// Function accessor
+ accessor={(user) => user.profile.age > 18}>
+
+
+
+// Undefined accessor (returns whole object)
+ accessor={undefined}>
+ ...
+
+```
+
+## Related
+
+- [ContentValue](./Contents.md#contentvalue) - Component using accessors
+- [FieldContent](./Contents.md#fieldcontent) - Simplified object field access
+- [Content Context](./ContentContext.md) - Context system foundation
+- [Micro-Context Pattern](./MICRO-CONTEXT.md) - Pattern overview
diff --git a/packages/ctablex-core/docs/ArrayContent.md b/packages/ctablex-core/docs/ArrayContent.md
new file mode 100644
index 0000000..737b970
--- /dev/null
+++ b/packages/ctablex-core/docs/ArrayContent.md
@@ -0,0 +1,468 @@
+# Array Content Components
+
+Components for iterating over arrays and accessing array indices.
+
+## TL;DR
+
+- Use `` to iterate over arrays
+- Use `` to display the current array index
+- Use `IndexContext`, `useIndex` to access the current index
+- Use `` to render content when the array is empty
+- Use `` to render content when the array is not empty
+
+## ArrayContent
+
+Iterates over an array, rendering children for each element. Provides both the array element and its index via context.
+
+### Props
+
+```tsx
+interface ArrayContentProps {
+ getKey?: PathAccessorTo | ArrayGetKey;
+ children?: ReactNode;
+ join?: ReactNode;
+ value?: ReadonlyArray;
+}
+
+type ArrayGetKey = (value: V, index: number) => string | number;
+```
+
+- **`getKey`** - Extracts a unique key from each element (optional, defaults to index)
+ - Path string: `"id"` extracts the `id` property
+ - Function: `(value, index) => value.id` for custom logic
+- **`children`** - Content to render for each element (defaults to ``)
+- **`join`** - Content to render between elements (e.g., commas, separators)
+- **`value`** - Array to iterate (optional, uses context value if omitted)
+
+### Behavior
+
+- Iterates over the array from the content context
+- Provides the array index via `IndexContext`
+- Provides each element via `ContentProvider`
+- Renders `join` content between elements (not before the first element)
+- Uses `getKey` to generate React keys for list items
+
+### Example
+
+```tsx
+import { ArrayContent, IndexContent } from '@ctablex/core';
+
+type User = {
+ id: number;
+ name: string;
+};
+
+const users: User[] = [
+ { id: 1, name: 'Alice' },
+ { id: 2, name: 'Bob' },
+];
+
+// Basic iteration
+
+
+
+
+
+
+
+// Renders: AliceBob
+
+// With separator
+
+
+
+
+
+
+
+// Renders: Alice, Bob
+
+// With index
+
+
+ .
+
+
+// Renders: 1. Alice2. Bob
+
+// Custom key function
+
+ `user-${user.id}`}>
+
+
+
+
+
+
+
+// Renders: Alice
Bob
+```
+
+### Type Safety
+
+The generic type `V` on `ArrayContent` provides type safety for the `getKey` prop:
+
+```tsx
+// ✓ Type-safe getKey with autocomplete
+ getKey="id"> {/* ✓ Autocomplete suggests "id", "name" */}
+ ...
+
+
+// ✗ Compile error - invalid path
+ getKey="email"> {/* ✗ Error! "email" doesn't exist on User */}
+ ...
+
+
+// ✓ Type-safe function
+ getKey={(user, index) => user.id}> {/* ✓ "user" is typed as User */}
+ ...
+
+```
+
+However, **nested components are not automatically type-checked**:
+
+```tsx
+// ✗ No type checking - nested FieldContent doesn't inherit User type
+>
+ {/* No error, but no autocomplete either */}
+
+
+
+
+// ✓ Type-safe with explicit generic on FieldContent
+>
+ field="name"> {/* ✓ "name" exists on User - autocomplete works! */}
+
+
+
+
+// ✗ Compile error with explicit generic
+>
+ field="email"> {/* ✗ Error! "email" doesn't exist on User */}
+
+
+
+```
+
+**Summary:**
+
+- `ArrayContent` generic provides type safety for `getKey` prop
+- Nested components need their own explicit generic like `>` for type checking
+
+## IndexContent
+
+Displays the current array index from `IndexContext`.
+
+### Props
+
+```tsx
+interface IndexContentProps {
+ start?: number;
+}
+```
+
+- **`start`** - Offset to add to the index (optional, defaults to 0)
+
+### Behavior
+
+- Retrieves the current index from `IndexContext`
+- Adds the `start` offset to the index
+- Renders the resulting number
+
+### Example
+
+```tsx
+const items = ['Apple', 'Banana', 'Cherry'];
+
+// Zero-based index
+
+
+ :
+
+
+// Renders: 0: Apple1: Banana2: Cherry
+
+// One-based index
+
+
+ .
+
+
+// Renders: 1. Apple2. Banana3. Cherry
+```
+
+### Error Handling
+
+Throws an error if used outside `IndexContext`:
+
+```tsx
+// ✗ Error: useIndex must be used within a IndexContext
+
+```
+
+## IndexContext
+
+React Context providing the current array index.
+
+```tsx
+const IndexContext: React.Context;
+function useIndex(): number;
+```
+
+### Behavior
+
+- Automatically provided by `ArrayContent` and `ObjectContent`
+- Contains the current iteration index
+- `useIndex()` throws if context is undefined
+
+### Example
+
+```tsx
+import { useIndex } from '@ctablex/core';
+
+function CustomIndexDisplay() {
+ const index = useIndex();
+ return Item #{index + 1};
+}
+
+
+
+ -
+
+;
+// Renders: Item #1 - AItem #2 - BItem #3 - C
+```
+
+## EmptyContent
+
+Conditionally renders children when the content value is `null`, `undefined`, or empty (by default, empty arrays).
+
+### Props
+
+```tsx
+interface EmptyContentProps {
+ children?: ReactNode;
+ isEmpty?: (content: C) => boolean;
+}
+```
+
+- **`children`** - Content to render when value is empty
+- **`isEmpty`** - Custom function to determine if content is empty (defaults to checking for empty arrays)
+
+### Behavior
+
+- Retrieves the content value from context
+- Renders children if value is `null`, `undefined`, or satisfies the `isEmpty` predicate
+- Returns `null` otherwise (renders nothing)
+- Default `isEmpty` function: `Array.isArray(content) && content.length === 0`
+
+### Example
+
+```tsx
+import { EmptyContent, ContentProvider } from '@ctablex/core';
+
+// Renders for null
+
+
+ No items
+
+
+// Renders: No items
+
+// Renders for undefined
+
+
+ No items
+
+
+// Renders: No items
+
+// Renders for empty array (default behavior)
+
+
+ No items
+
+
+// Renders: No items
+
+// Renders nothing for non-empty array
+
+
+ No items
+
+
+// Renders: (nothing)
+
+// Custom isEmpty predicate
+
+ s.length === 0}>
+ String is empty
+
+
+// Renders: String is empty
+```
+
+### Custom isEmpty Function
+
+Define custom logic to determine when content is considered empty:
+
+```tsx
+type Product = {
+ name: string;
+ items: string[];
+};
+
+// Custom isEmpty for objects
+
+ p.items.length === 0}>
+ No products in inventory
+
+
+
+// Custom isEmpty for strings
+
+ s.trim().length === 0}>
+ String is blank
+
+
+```
+
+### Combining with NullableContent
+
+To show different content for `null`/`undefined` versus empty arrays, combine `NullableContent` and `EmptyContent`:
+
+```tsx
+
+ Empty
+
+
+```
+
+## NonEmptyContent
+
+Conditionally renders children when the content value is **not** `null`, `undefined`, or empty. Inverse of `EmptyContent`.
+
+### Props
+
+```tsx
+interface NonEmptyContentProps {
+ children?: ReactNode;
+ isEmpty?: (content: C) => boolean;
+}
+```
+
+- **`children`** - Content to render when value is not empty
+- **`isEmpty`** - Custom function to determine if content is empty (defaults to checking for empty arrays)
+
+### Behavior
+
+- Retrieves the content value from context
+- Renders `null` (nothing) if value is `null`, `undefined`, or satisfies the `isEmpty` predicate
+- Renders children otherwise
+- Default `isEmpty` function: `Array.isArray(content) && content.length === 0`
+
+### Example
+
+```tsx
+import { NonEmptyContent, ContentProvider } from '@ctablex/core';
+
+// Renders nothing for null
+
+
+ Has data
+
+
+// Renders: (nothing)
+
+// Renders nothing for empty array
+
+
+ Has items
+
+
+// Renders: (nothing)
+
+// Renders children for non-empty array
+
+
+ Has items
+
+
+// Renders: Has items
+
+// Custom isEmpty with objects
+
+ obj.items.length === 0}>
+ Inventory has products
+
+
+// Renders: Inventory has products
+```
+
+### Use Cases
+
+- Display content only when data is available
+- Wrap arrays with DOM elements only when not empty
+- Apply custom `isEmpty` logic for different data types
+
+#### Wrapping Arrays with DOM Elements
+
+`NonEmptyContent` is especially useful when you want to wrap array content with DOM elements only when the array is not empty:
+
+```tsx
+type Order = {
+ id: string;
+ items: string[];
+};
+
+const order: Order = {
+ id: '123',
+ items: ['Widget', 'Gadget'],
+};
+
+
+
+
+
+
+
+ Order is empty
+
+
+;
+// Renders:
+// with empty items, renders: Order is empty
+```
+
+### Combining Empty and NonEmpty
+
+Use both components together to handle all cases:
+
+```tsx
+
+
+ No results found
+
+
+ Results:
+
+
+
+
+
+
+
+```
+
+## Related
+
+- [ObjectContent](./ObjectContent.md) - Iterate over object properties
+- [ContentContext](./ContentContext.md) - Content value context
+- [Accessors](./Accessors.md) - Type-safe value extraction
+- [Micro-Context Pattern](./MICRO-CONTEXT.md) - Pattern overview
diff --git a/packages/ctablex-core/docs/ContentContext.md b/packages/ctablex-core/docs/ContentContext.md
new file mode 100644
index 0000000..abca0e8
--- /dev/null
+++ b/packages/ctablex-core/docs/ContentContext.md
@@ -0,0 +1,391 @@
+# Content Context
+
+The Content Context system provides the foundation for the micro-context pattern, enabling data flow through React Context instead of props. It consists of three parts that work together:
+
+- **`ContentContext`** - The underlying React Context
+- **`ContentProvider`** - Component to provide values via context
+- **`useContent`** - Hook to consume values from context
+
+## TL;DR
+
+Use `` to provide any value and make it available to descendant components. Use `useContent` within those components to access the provided value. `useContent` can also accept an optional override value.
+
+## Basic Usage
+
+```tsx
+import { ContentProvider, useContent } from '@ctablex/core';
+
+function App() {
+ const user = { name: 'John', age: 30 };
+
+ return (
+
+
+
+ );
+}
+
+function UserDisplay() {
+ const user = useContent<{ name: string; age: number }>();
+ return (
+
+ {user.name} is {user.age} years old
+
+ );
+}
+```
+
+## ContentProvider
+
+Wraps any value in a React Context, making it available to descendant components.
+
+### Props
+
+```tsx
+interface ContentProviderProps {
+ value: V;
+ children?: ReactNode;
+}
+```
+
+#### `value`
+
+**Type:** `V` (generic)
+
+The data to provide via context. Can be any type - primitives, objects, arrays, etc.
+
+```tsx
+
+
+
+
+
+
+
+
+
+
+
+```
+
+#### `children`
+
+**Type:** `ReactNode` (optional)
+
+Components that will have access to the provided value via `useContent`.
+
+### Nesting
+
+Providers can be nested to create scoped contexts. Child providers override parent values:
+
+```tsx
+
+ {/* accesses "outer" */}
+
+ {/* accesses "inner" */}
+
+ {/* accesses "outer" */}
+
+```
+
+This nesting is the core of micro-context's data transformation pattern:
+
+```tsx
+
+ {/* user context */}
+
+ {/* address context */}
+
+ {/* city context */}
+
+
+
+
+```
+
+### Performance
+
+`ContentProvider` uses `useMemo` internally to prevent unnecessary re-renders when the same value reference is provided.
+
+**Best practice:** Memoize or stabilize the `value` prop when possible:
+
+```tsx
+// ✗ Creates new object every render - triggers context updates
+function App() {
+ return ...;
+}
+
+// ✓ Stable reference - no unnecessary updates
+const user = { name: 'John' };
+function App() {
+ return ...;
+}
+
+// ✓ Memoized when dependent on props/state
+function App({ userId }) {
+ const user = useMemo(() => getUser(userId), [userId]);
+ return ...;
+}
+```
+
+## useContent
+
+Retrieves the current value from the nearest `ContentProvider` in the component tree.
+
+### Signature
+
+```tsx
+function useContent(value?: V): V;
+```
+
+### Type Parameter
+
+#### `V`
+
+The expected type of the content value. **This is purely manual** - TypeScript cannot verify it matches the actual context value.
+
+```tsx
+// Type is a hint to TypeScript, not validated
+const user = useContent(); // ✗ No compile-time validation
+
+// Wrong type compiles successfully
+const user = useContent(); // ✗ No error!
+```
+
+### Parameter
+
+#### `value` (optional)
+
+**Type:** `V | undefined`
+
+An optional override value. If provided, this value is returned instead of the context value.
+
+```tsx
+function Display({ value }: { value?: number }) {
+ const content = useContent(value);
+ return {content};
+}
+
+// Uses override value
+
+
+// Uses context value
+
+ {/* Displays: 99 */}
+
+```
+
+**Use case:** Allows components to work both standalone (with props) and within context.
+
+### Error Handling
+
+`useContent` throws an error if called outside a `ContentProvider`:
+
+```tsx
+function Component() {
+ const value = useContent(); // ✗ Error: useContent must be used within a ContentContext
+ return {value}
;
+}
+```
+
+**Exception:** If an override `value` parameter is provided, no error is thrown:
+
+```tsx
+function Component() {
+ const value = useContent(42); // ✓ Returns 42, no context needed
+ return {value}
;
+}
+```
+
+## ContentContext
+
+The underlying React Context that powers `ContentProvider` and `useContent`.
+
+**⚠️ Internal API:** `ContentContext` is an internal implementation detail and may change in future versions. Always prefer using `ContentProvider` and `useContent` in application code.
+
+### Type
+
+```tsx
+type ContentContextType = { value: V };
+
+const ContentContext: React.Context | undefined>;
+```
+
+### When to Use Directly
+
+**Most applications should use `ContentProvider` and `useContent` instead.** Direct context usage is only needed for advanced scenarios:
+
+#### Optional Context Consumption
+
+When you want to handle missing context gracefully without throwing an error:
+
+```tsx
+import { ContentContext } from '@ctablex/core';
+import { useContext } from 'react';
+
+function OptionalDisplay() {
+ const context = useContext(ContentContext);
+
+ if (!context) {
+ return No data provided
;
+ }
+
+ return {context.value}
;
+}
+```
+
+Compare with `useContent`, which throws an error when context is missing.
+
+#### Custom Context Logic
+
+When building your own abstractions:
+
+```tsx
+function useContentOrDefault(defaultValue: V): V {
+ const context = useContext(ContentContext);
+ return context ? context.value : defaultValue;
+}
+```
+
+#### Context Consumer Pattern
+
+Legacy consumer pattern instead of hooks:
+
+```tsx
+
+ {(context) => (context ? {context.value}
: No context
)}
+
+```
+
+## Type Safety Limitations
+
+### No Type Inference or Validation
+
+The hook cannot infer the type from context, and TypeScript cannot verify that the type parameter matches the actual context value:
+
+```tsx
+type User = { name: string };
+
+function Component() {
+ // Must manually specify type - no inference
+ const user = useContent();
+ return {user.name}
;
+}
+
+// ✓ Correct usage
+
+
+
+
+// ✗ Type mismatch, but compiles! Runtime error!
+
+
+
+```
+
+The problem is that `useContent()` accepts any type parameter, and there's no way for TypeScript to validate it against the actual context value at compile time.
+
+### Refactoring Risks
+
+Renaming fields won't automatically update all usages. Manual verification is required throughout the component tree.
+
+## Best Practices
+
+### Always Specify Type Parameter
+
+```tsx
+// ✗ Avoid - type is `any`
+const value = useContent();
+
+// ✓ Better - explicit type
+const value = useContent();
+```
+
+### Use Close to Context Provider
+
+The further `useContent` is from its provider, the harder it is to verify type correctness:
+
+```tsx
+// ✓ Easy to verify type
+
+ {/* Uses useContent() */}
+
+
+// ✗ Harder to track - many layers deep
+
+
+
+
+ {/* Uses useContent??> - what type? */}
+
+
+
+
+```
+
+### Consider Using Override Parameter
+
+For components that should work both with and without context:
+
+```tsx
+function Display({ price }: { price?: number }) {
+ const value = useContent(price);
+ return ${value.toFixed(2)};
+}
+
+// Works standalone
+
+
+// Works with context
+
+
+
+```
+
+### Use High-Level APIs
+
+Prefer `ContentProvider` and `useContent` over direct `ContentContext` usage. They provide better error messages, simpler API, and built-in optimizations.
+
+## Examples
+
+### Simple Value Display
+
+```tsx
+function PriceDisplay() {
+ const price = useContent();
+ return ${price.toFixed(2)};
+}
+
+
+ {/* Displays: $99.99 */}
+;
+```
+
+### With Override Parameter
+
+```tsx
+function UserGreeting({ name }: { name?: string }) {
+ const userName = useContent(name);
+ return Hello, {userName}!
;
+}
+
+// Standalone with prop
+
+
+// With context
+
+
+
+
+// Context with override (override wins)
+
+ {/* Uses "Dave", not "Charlie" */}
+
+```
+
+## Related
+
+- [Micro-Context Pattern](./MICRO-CONTEXT.md) - Pattern overview
+- [FieldContent](./Contents.md#fieldcontent) - Access object fields
+- [ArrayContent](./ArrayContent.md) - Map arrays
+- [DefaultContent](./Contents.md#defaultcontent) - Render primitive values
diff --git a/packages/ctablex-core/docs/Contents.md b/packages/ctablex-core/docs/Contents.md
new file mode 100644
index 0000000..888a1f4
--- /dev/null
+++ b/packages/ctablex-core/docs/Contents.md
@@ -0,0 +1,538 @@
+# Content Components
+
+Utility components for accessing fields, transforming values, and handling null/undefined.
+
+## TL;DR
+
+- Use `` to render primitive values
+- Use `` for flexible value transformation (paths, functions)
+- Use `` to access object properties
+- Use `` and `` for null/undefined handling
+
+## DefaultContent
+
+Renders primitive values (string, number, null, undefined) directly.
+
+### Props
+
+None.
+
+### Behavior
+
+- Retrieves the content value from context
+- Renders it directly (React's default behavior for primitives)
+- Used as the default `children` for most content components
+- **Only works with primitives** - objects and arrays of objects will cause React errors
+- Arrays of primitives work fine
+
+### Example
+
+```tsx
+import { DefaultContent } from '@ctablex/core';
+
+// String
+
+ {/* Renders: Hello */}
+
+
+// Number
+
+ {/* Renders: 42 */}
+
+
+// Boolean (renders nothing - React's default)
+
+ {/* Renders: (nothing) */}
+
+
+// Null
+
+ {/* Renders: (nothing) */}
+
+
+// ✗ Common mistake - objects cause React errors
+
+ {/* ✗ Error: Objects are not valid as a React child */}
+
+
+// ✓ Use FieldContent or ObjectContent for objects
+
+
+
+
+
+```
+
+### Default Usage
+
+Most content components use `` as their default children:
+
+```tsx
+// These are equivalent:
+
+
+
+
+
+// Also equivalent:
+
+
+
+
+```
+
+### Common Pitfall
+
+Many content components use `` as default children. This causes errors when the content value is an object or array of objects:
+
+```tsx
+// ✗ Error - ArrayContent provides array elements (objects) to DefaultContent
+
+ {/* Missing children - defaults to */}
+
+// ✗ Error: Objects are not valid as a React child
+
+// ✓ Provide explicit children for object/array content
+
+
+
+
+
+
+
+
+// ✗ Error - FieldContent provides object value to DefaultContent
+
+ {/* Missing children - defaults to */}
+
+
+// ✗ Error: Objects are not valid as a React child
+
+// ✓ Nest FieldContent or use ObjectContent
+
+
+
+
+
+
+
+```
+
+**Remember:** `` only works with primitives (string, number, boolean, null, undefined) or arrays of primitives. For objects and arrays, you must provide explicit children.
+
+**Note on booleans:** While `` accepts boolean values without error, React renders nothing for `true` or `false` by default. To display boolean values, provide a custom component:
+
+```tsx
+// React renders nothing for booleans
+
+ {/* Renders: (nothing) */}
+
+
+// ✓ Use a custom component to display boolean values
+
+ {/* Renders: Yes */}
+
+// Renders: Yes
+```
+
+## ContentValue
+
+Transforms the content value using an accessor (path, function, undefined, or null), then provides the result to children.
+
+> **Note:** This component was previously named `AccessorContent`. The old name is still available as an alias for backward compatibility.
+
+### Props
+
+```tsx
+interface ContentValueProps {
+ accessor: Accessor;
+ children?: ReactNode;
+ value?: V;
+}
+```
+
+- **`accessor`** - Accessor to apply to the content value
+ - Path string: `"user.address.city"`
+ - Function: `(value) => value.computed`
+ - `undefined`: Returns value unchanged
+ - `null`: Returns null
+- **`children`** - Content to render with transformed value (defaults to ``)
+- **`value`** - Input value to transform (optional, uses context value if omitted)
+
+### Behavior
+
+- Retrieves the content value from context (or uses `value` prop)
+- Applies the accessor to transform the value
+- Provides the transformed value to children via a new `ContentProvider`
+
+### Example
+
+```tsx
+import { ContentValue, DefaultContent } from '@ctablex/core';
+
+type User = {
+ profile: {
+ name: string;
+ age: number;
+ };
+ isActive: boolean;
+};
+
+const user: User = {
+ profile: { name: 'Alice', age: 30 },
+ isActive: true,
+};
+
+// Path accessor
+
+
+ {/* Renders: Alice */}
+
+
+
+// Function accessor
+
+ u.profile.age > 18}>
+ {/* Renders: Yes */}
+
+
+
+// Undefined accessor (returns unchanged)
+
+
+
+ {/* Renders: true */}
+
+
+
+
+// Null accessor
+
+
+
+
+
+
+
+// Renders: No value
+```
+
+### Type Safety
+
+The `accessor` prop is validated based on the generic type:
+
+```tsx
+type Product = {
+ name: string;
+ price: number;
+ metadata: {
+ weight: number;
+ };
+};
+
+// ✓ Type-safe paths with autocomplete
+ accessor="metadata.weight">
+
+
+
+// ✗ Compile error - invalid path
+ accessor="metadata.height">
+
+
+
+// ✓ Type-safe function
+ accessor={(p) => p.price * 1.1}>
+
+
+```
+
+**Note:** The generic type `` itself is not validated by TypeScript (you could pass the wrong type), but **given** that type, the `accessor` prop is fully validated with autocomplete.
+
+## FieldContent
+
+Accesses a single field of an object and provides its value to children. Simplified version of `ContentValue` for object properties.
+
+### Props
+
+```tsx
+interface FieldContentProps {
+ field: keyof V;
+ children?: ReactNode;
+}
+```
+
+- **`field`** - The object property to access
+- **`children`** - Content to render with field value (defaults to ``)
+
+### Behavior
+
+- Retrieves the content value from context
+- Accesses the specified field
+- Provides the field value to children via a new `ContentProvider`
+
+### Example
+
+```tsx
+import { FieldContent, DefaultContent } from '@ctablex/core';
+
+type User = {
+ name: string;
+ email: string;
+ age: number;
+ address: {
+ city: string;
+ };
+};
+
+const user: User = {
+ name: 'Bob',
+ email: 'bob@example.com',
+ age: 25,
+ address: {
+ city: 'New York',
+ },
+};
+
+// Access single field
+
+
+ {/* Renders: Bob */}
+
+
+
+// Nested field access (field within field)
+
+
+
+ {/* Renders: New York */}
+
+
+
+
+// Multiple fields
+
+ Name:
+ Email:
+
+// Renders: Name: Bob, Email: bob@example.com
+
+
+// ✗ Common mistake - trying to nest fields from parent context
+
+
+ Name: ,
+ Email:
+
+
+// ✗ Error! "email" is a string, not an object with a "name" field
+```
+
+### Type Safety
+
+The `field` prop is validated based on the generic type:
+
+```tsx
+type Product = {
+ name: string;
+ price: number;
+};
+
+// ✓ Valid field with autocomplete
+ field="name">
+
+
+
+// ✗ Compile error - field doesn't exist
+ field="description">
+
+
+```
+
+**Note:** You must add the generic type `` to get type checking and autocomplete for the `field` prop. Without it, no validation occurs:
+
+```tsx
+// ✗ No type checking - accepts any string
+
+
+
+```
+
+### Comparison with ContentValue
+
+`FieldContent` is simpler but less flexible:
+
+```tsx
+// FieldContent - simple, direct property access
+
+
+
+
+// ContentValue - more flexible, supports nested paths
+
+
+
+```
+
+## NullableContent
+
+Conditionally renders content based on whether the value is null or undefined.
+
+### Props
+
+```tsx
+interface NullableContentProps {
+ children?: ReactNode;
+ nullContent?: ReactNode;
+}
+```
+
+- **`children`** - Content to render when value is not null/undefined (defaults to ``)
+- **`nullContent`** - Content to render when value is null/undefined (defaults to `null`)
+
+### Behavior
+
+- Retrieves the content value from context
+- If value is `null` or `undefined`, renders `nullContent`
+- Otherwise, renders `children`
+
+### Example
+
+```tsx
+import { NullableContent, DefaultContent } from '@ctablex/core';
+
+type User = {
+ name: string;
+ email?: string;
+};
+
+const user1: User = { name: 'Alice', email: 'alice@example.com' };
+const user2: User = { name: 'Bob', email: undefined };
+
+// With value
+
+
+
+
+
+
+
+// Renders: alice@example.com
+
+// Without value
+
+
+
+
+
+
+
+// Renders: No email
+
+// Default behavior (renders nothing for null)
+
+
+
+
+
+// Renders: (nothing)
+```
+
+### Use Cases
+
+- Display fallback text for missing optional fields
+- Hide content when data is unavailable
+
+## NullContent
+
+Conditionally renders children only when the content value is `null` or `undefined`.
+
+`NullContent` is useful for rendering complex fallback content when the value is missing. It is the opposite of `NullableContent`, which renders content when the value exists. For simpler cases, you can use the `nullContent` prop of `NullableContent`.
+
+### Props
+
+```tsx
+interface NullContentProps {
+ children?: ReactNode;
+}
+```
+
+- **`children`** - Content to render when value is null or undefined
+
+### Behavior
+
+- Retrieves the content value from context
+- Renders children if value is `null` or `undefined`
+- Returns `null` otherwise (renders nothing)
+
+### Example
+
+```tsx
+import { NullContent, ContentProvider } from '@ctablex/core';
+
+// Renders children when null
+
+
+ No data available
+
+
+// Renders: No data available
+
+// Renders children when undefined
+
+
+ No data available
+
+
+// Renders: No data available
+
+// Renders nothing when value exists
+
+
+ No data available
+
+
+// Renders: (nothing)
+
+// Renders nothing for empty string
+
+
+ No data available
+
+
+// Renders: (nothing) - empty string is not null/undefined
+```
+
+### Use Cases
+
+- Display fallback UI when data is missing
+- Render complex content when value is null (for simpler cases, use the `nullContent` prop of `NullableContent`)
+
+```tsx
+type User = {
+ name: string;
+ email?: string;
+};
+
+const user: User = { name: 'Alice' };
+
+
+
+
+ Email not provided
+
+
+
+
+
+;
+
+// Renders: Email not provided
+```
+
+## Related
+
+- [ArrayContent](./ArrayContent.md) - Iterate over arrays
+- [ObjectContent](./ObjectContent.md) - Iterate over objects
+- [ContentContext](./ContentContext.md) - Content value context
+- [Accessors](./Accessors.md) - Type-safe value extraction
+- [Micro-Context Pattern](./MICRO-CONTEXT.md) - Pattern overview
diff --git a/packages/ctablex-core/docs/MICRO-CONTEXT.md b/packages/ctablex-core/docs/MICRO-CONTEXT.md
new file mode 100644
index 0000000..b7ffc7c
--- /dev/null
+++ b/packages/ctablex-core/docs/MICRO-CONTEXT.md
@@ -0,0 +1,326 @@
+# Micro-Context Pattern
+
+## What is Micro-Context?
+
+**Micro-context** is a pattern for passing data through localized React Context instead of props. Unlike traditional "macro-context" patterns (like theme providers or auth state that span entire applications), micro-context creates small, scoped context providers within component subtrees for fine-grained data flow.
+
+This enables **declarative data transformation** with minimal manual prop passing—components describe what data they need, not how to pass it through every layer.
+
+## The Problem It Solves
+
+In traditional React patterns, data flows through props manually:
+
+```tsx
+
+ {data.map((item) => (
+
+ |
+
+ |
+
+ ))}
+
+```
+
+This leads to:
+
+- **Prop drilling** - Every intermediate component must accept and pass props
+- **Tight coupling** - Child components are explicitly bound to parent props
+- **Limited composition** - Hard to create reusable renderers that work in different contexts
+
+## The Micro-Context Solution
+
+Instead of passing data as props, wrap it in a context provider, no data passed via props or manual iteration:
+
+```tsx
+
+
+ {/* Iterates array, provides each item via context */}
+
+
+ {/* Extracts "price" field, provides it via context */}
+
+ |
+ {/* Reads value from context */}
+
+ |
+
+
+
+
+
+```
+
+Notice that no props are passed through `Table`, `Row`, or `Cell`. Each component declares what data it needs, and micro-context handles the flow automatically. This enables powerful patterns for building flexible, composable components.
+
+Child components access data through hooks:
+
+```tsx
+function NumberFormatter() {
+ const value = useContent(); // gets value from nearest context
+ return <>{formatNumber(value)}>;
+}
+```
+
+## Key Characteristics
+
+### 1. **Reusable Renderers**
+
+Components that consume context work anywhere without knowing the data source:
+
+```tsx
+function BooleanContent({ yes, no }) {
+ const value = useContent();
+ return <>{value ? yes : no}>;
+}
+
+// Works in any context that provides a boolean
+
+
+;
+```
+
+### 2. **Immutable Children & Performance**
+
+Since data flows through context instead of props, React elements often have no changing props, making them **immutable**. This enables powerful optimizations:
+
+```tsx
+// Children can be memoized
+const defaultChildren = ;
+
+function FieldContent({ field }) {
+ const content = useContent();
+ return (
+
+ {defaultChildren} {/* Same reference every render */}
+
+ );
+}
+
+// Or memoized within the component
+function ProductTable() {
+ return useMemo(
+ () => (
+
+
+
+
+
+ ),
+ [],
+ );
+}
+
+// Or even moved outside the component
+const content = (
+
+
+
+
+
+);
+// component
+function ProductTable() {
+ return content;
+}
+```
+
+Immutable children help React's reconciliation algorithm skip unnecessary re-renders and comparisons.
+
+### 3. **Default Children**
+
+Components can provide sensible defaults, reducing boilerplate:
+
+```tsx
+// With default children
+
+
+// Equivalent to
+
+
+
+
+// Implementation
+const defaultChildren = ;
+function ContentValue({ accessor, children = defaultChildren }) {
+ const content = useContent();
+ return (
+
+ {children}
+
+ );
+}
+```
+
+This makes simple cases concise while keeping customization available.
+
+### 4. **Open for Customization**
+
+Default children create a pattern where components work out-of-the-box but remain fully customizable:
+
+```tsx
+// Default usage - simple and clean
+
+
+// Custom rendering - override when needed
+
+
+
+
+// Complex composition - mix defaults and custom
+
+
+
+ :
+
+
+
+```
+
+This pattern balances **ease of use** (good defaults) with **flexibility** (full customization).
+
+## Core Concepts
+
+### ContentProvider & useContent
+
+The foundation of micro-context:
+
+```tsx
+// Provide data via context
+
+
+;
+
+// Consume data from context
+function MyComponent() {
+ const data = useContent();
+ return {data}
;
+}
+```
+
+### Content Components
+
+Components that transform data and provide it via context:
+
+- **ContentValue** - Extract data using path or function accessors
+- **FieldContent** - Access object fields
+- **ArrayContent** - Map arrays, providing each item via context
+- **ObjectContent** - Iterate object keys, providing values via context
+- **NullableContent** - Handle null/undefined values
+- **DefaultContent** - Render primitive values
+
+### Accessors
+
+Extract values from data structures:
+
+```tsx
+// Path accessor - string-based
+
+
+
+
+// Function accessor
+ user.fullName}>
+
+
+```
+
+### Additional Contexts
+
+Beyond value context, micro-contexts can provide metadata:
+
+- **IndexContext** - Current index in array/object iteration
+- **KeyContext** - Current key in object iteration
+
+```tsx
+
+ {/* renders 0, 1, 2... */}
+
+
+```
+
+## Trade-offs and Limitations
+
+### Weak Type Safety
+
+The biggest drawback of micro-context is the **lack of strong type safety**. While TypeScript provides autocomplete and validation for props, it cannot enforce correctness across context boundaries.
+
+#### How It Works
+
+TypeScript type safety in ctablex has three key characteristics:
+
+1. **Generic types on components are NOT validated** - You can pass wrong types without errors
+2. **Props ARE validated based on the generic** - Given a type, props get autocomplete
+3. **Nested components are NOT automatically typed** - Must add explicit generics to each
+
+This means **generic types must be explicitly passed** to both content components and the `useContent` hook:
+
+```tsx
+type User = { name: string; address: { city: string } };
+
+// ✓ Type-safe with explicit generic - field is validated and autocompleted
+ field="name">
+
+
+
+// ✗ No type checking without generic - any field accepted
+
+
+
+
+// ✓ Nested components need their own generics
+ getKey="id">
+ field="name" /> {/* Must specify User again */}
+
+
+// useContent also requires manual type annotation
+function MyComponent() {
+ const user = useContent(); // ✗ Type is just a hint - not validated
+ return {user.name}
;
+}
+```
+
+#### Why This Happens
+
+- **JSX elements types are erased at compile time** to `ReactElement` so no type information flows to children
+- **JSX elements cannot be validated at compile time** to infer or verify generics
+- **TypeScript cannot verify that the generic type matches the actual context value**
+- **`useContent()` type is purely manual** - TypeScript cannot check it matches context
+- **Type safety depends entirely on developer discipline**
+
+#### Implications
+
+- **Runtime errors** - Typos in field names won't be caught at compile time
+- **Refactoring risks** - Renaming fields may not update all references
+- **Developer burden** - Must manually ensure type correctness throughout the component tree
+- **Silent failures** - Wrong types compile successfully but fail at runtime
+- **No autocomplete without generics** - IDEs can't suggest valid field names
+
+## When to Use Micro-Context
+
+Micro-context excels when:
+
+- Building **repetitive structures** (tables, lists)
+- Creating **reusable renderers** that work with different data
+- Providing **high customizability** - Components using micro-context enable flexible composition and customization
+
+## Micro vs Macro Context
+
+| Aspect | Macro-Context | Micro-Context |
+| -------- | ------------------------------- | ----------------------------- |
+| Scope | Application-wide | Component subtree |
+| Lifetime | Entire app lifecycle | Component render |
+| Updates | Infrequent, triggers re-renders | Frequent, localized |
+| Purpose | Global state (theme, auth) | Data transformation & flow |
+| Nesting | Typically 1-2 levels | Multiple nested levels |
+| Examples | ThemeProvider, AuthProvider | ContentProvider, ArrayContent |
+
+## Summary
+
+Micro-context is a pattern for **localized, granular data flow** using React Context. By creating small, scoped providers within component trees, it enables:
+
+- Data transformation without prop drilling
+- Reusable components that consume from context
+- Flexible composition of transformers and renderers
+
+This approach unlocks powerful patterns for building flexible, composable UIs while maintaining clean component boundaries. However, this flexibility comes at the cost of weaker compile-time type safety compared to traditional props.
diff --git a/packages/ctablex-core/docs/ObjectContent.md b/packages/ctablex-core/docs/ObjectContent.md
new file mode 100644
index 0000000..148d20a
--- /dev/null
+++ b/packages/ctablex-core/docs/ObjectContent.md
@@ -0,0 +1,241 @@
+# Object Content Components
+
+Components for iterating over object properties and accessing object keys.
+
+## TL;DR
+
+- Use `` to iterate over object properties
+- Use `` to display the current property key
+- Use `KeyContext`, `useKey` to access the current property key
+
+## ObjectContent
+
+Iterates over object properties, rendering children for each key-value pair. Provides the property value, key, and index via context.
+
+### Props
+
+```tsx
+interface ObjectContentProps {
+ getKey?: ObjectGetKey;
+ children: ReactNode;
+ join?: ReactNode;
+ value?: V;
+}
+
+type ObjectGetKey = (
+ value: V[K],
+ key: K,
+ index: number,
+) => string | number;
+```
+
+- **`getKey`** - Generates a unique React key for each property (optional, defaults to property key)
+- **`children`** - Content to render for each property (required)
+- **`join`** - Content to render between properties (e.g., commas, separators)
+- **`value`** - Object to iterate (optional, uses context value if omitted)
+
+### Behavior
+
+- Iterates over `Object.keys()` of the object from content context
+- Provides each property value via `ContentProvider`
+- Provides the property key via `KeyContext`
+- Provides the iteration index via `IndexContext`
+- Renders `join` content between properties (not before the first property)
+- Uses `getKey` to generate React keys for list items (defaults to `key.toString()`)
+
+### Example
+
+```tsx
+import { ObjectContent, KeyContent, IndexContent } from '@ctablex/core';
+
+type Product = {
+ name: string;
+ price: number;
+ stock: number;
+};
+
+const product: Product = {
+ name: 'Widget',
+ price: 99.99,
+ stock: 50,
+};
+
+// Basic iteration
+
+
+ :
+
+
+// Renders: name: Widgetprice: 99.99stock: 50
+
+// With separator
+
+
+ :
+
+
+// Renders: name: Widget, price: 99.99, stock: 50
+
+// With index
+
+ }>
+ . :
+
+
+// Renders:
+// 1. name: Widget
+// 2. price: 99.99
+// 3. stock: 50
+
+// Custom key function
+
+ `prop-${index}`}>
+ :
+
+
+```
+
+### Type Safety
+
+The generic type `V` on `ObjectContent` provides type safety for the `getKey` prop:
+
+```tsx
+type User = {
+ name: string;
+ age: number;
+};
+
+// ✓ Type-safe getKey function
+
+ getKey={(value, key, index) => {
+ // value: string | number (union of User property types)
+ // key: "name" | "age" (User keys)
+ return `${String(key)}-${index}`;
+ }}
+>
+ :
+;
+```
+
+However, **nested components do NOT receive type information** from `ObjectContent`:
+
+```tsx
+// Generic on ObjectContent alone doesn't provide type checking for nested components
+>
+ {/* string | number | symbol - always this type */}
+ {/* any - no type information from ObjectContent */}
+
+```
+
+**Summary:**
+
+- `ObjectContent` generic provides type safety for `getKey` prop
+- Nested components do NOT inherit the type - each needs its own explicit generic if type safety is needed
+
+## KeyContent
+
+Displays the current object property key from `KeyContext`.
+
+### Props
+
+None.
+
+### Behavior
+
+- Retrieves the current property key from `KeyContext`
+- Renders the key as a string
+
+### Example
+
+```tsx
+const user = {
+ firstName: 'John',
+ lastName: 'Doe',
+ age: 30,
+};
+
+
+ }>
+ :
+
+;
+// Renders:
+// firstName: John
+// lastName: Doe
+// age: 30
+```
+
+### Error Handling
+
+Throws an error if used outside `KeyContext`:
+
+```tsx
+// ✗ Error: useKey must be used within a KeyContext
+
+```
+
+## KeyContext
+
+React Context providing the current object property key.
+
+```tsx
+const KeyContext: React.Context;
+function useKey(): string | number | symbol;
+```
+
+### Behavior
+
+- Automatically provided by `ObjectContent`
+- Contains the current property key
+- `useKey()` throws if context is undefined
+
+### Example
+
+```tsx
+import { useKey } from '@ctablex/core';
+
+function CustomKeyDisplay() {
+ const key = useKey();
+ return {String(key).toUpperCase()};
+}
+
+const data = { name: 'Alice', age: 25 };
+
+
+
+ :
+
+;
+// Renders: NAME: Alice, AGE: 25
+```
+
+## IndexContext
+
+The iteration index is also provided via `IndexContext` (see [ArrayContent](./ArrayContent.md#indexcontext)).
+
+### Example
+
+```tsx
+const settings = {
+ theme: 'dark',
+ language: 'en',
+ notifications: true,
+};
+
+
+ }>
+ : =
+
+;
+// Renders:
+// 0: theme = dark
+// 1: language = en
+// 2: notifications = true
+```
+
+## Related
+
+- [ArrayContent](./ArrayContent.md) - Iterate over arrays
+- [ContentContext](./ContentContext.md) - Content value context
+- [FieldContent](./Contents.md#fieldcontent) - Access single object field
+- [Micro-Context Pattern](./MICRO-CONTEXT.md) - Pattern overview
diff --git a/packages/ctablex-core/docs/README.md b/packages/ctablex-core/docs/README.md
new file mode 100644
index 0000000..ede4025
--- /dev/null
+++ b/packages/ctablex-core/docs/README.md
@@ -0,0 +1,235 @@
+# Documentation Guide
+
+Welcome to the ctablex-core documentation! This guide will help you navigate and understand the documentation structure.
+
+## Quick Start
+
+If you're new to ctablex, start here:
+
+1. **[Micro-Context Pattern](./MICRO-CONTEXT.md)** - Understand the core concept behind ctablex
+2. **[ContentContext](./ContentContext.md)** - Learn about the foundation: ContentProvider and useContent
+3. **[Contents](./Contents.md)** - Explore basic content transformation components
+
+## Documentation Structure
+
+### Core Concepts
+
+#### [Micro-Context Pattern](./MICRO-CONTEXT.md)
+
+The fundamental pattern that ctablex is built on. Read this first to understand:
+
+- What micro-context means (localized context vs app-wide context)
+- Key characteristics: immutable children, default children, context nesting
+- Benefits and trade-offs (especially type safety limitations)
+- When to use this pattern
+
+### Foundation
+
+#### [ContentContext](./ContentContext.md)
+
+The building blocks of the micro-context system:
+
+- **ContentProvider** - Wraps values and provides them to children
+- **useContent** - Hook to retrieve values from context
+- **ContentContext** - Internal React Context (rarely used directly)
+
+### Content Components
+
+Components for transforming and accessing data:
+
+#### [Contents](./Contents.md)
+
+Utility components for common operations:
+
+- **ContentValue** - Transform values using paths or functions
+- **FieldContent** - Access object properties
+- **NullableContent**, **NullContent** - Handle null/undefined values
+- **DefaultContent** - Render primitive values
+
+#### [ArrayContent](./ArrayContent.md)
+
+Components for array iteration:
+
+- **ArrayContent** - Iterate over arrays with index support
+- **IndexContent** - Display current iteration index
+- **IndexContext** - Context providing array index
+- **EmptyContent**, **NonEmptyContent** - Conditional rendering based on array emptiness
+
+#### [ObjectContent](./ObjectContent.md)
+
+Components for object iteration:
+
+- **ObjectContent** - Iterate over object properties
+- **KeyContent** - Display current property key
+- **KeyContext** - Context providing property key
+
+### Type-Safe Data Access
+
+#### [Accessors](./Accessors.md)
+
+Strong type safety for value extraction:
+
+- **Path Accessors** - `accessByPath`, `accessByPathTo` with autocomplete
+- **Function Accessors** - `accessByFn` with type inference
+- **Unified Accessors** - `access`, `accessTo` accepting any accessor type
+- **Type Definitions** - `PathAccessor`, `Accessor`, and related types
+
+## Common Patterns
+
+### Basic Value Display
+
+```tsx
+
+
+
+
+
+```
+
+Start with: [Contents](./Contents.md#fieldcontent)
+
+### Array Iteration
+
+```tsx
+
+
+
+
+
+
+
+```
+
+Start with: [ArrayContent](./ArrayContent.md)
+
+### Object Iteration
+
+```tsx
+
+
+ :
+
+
+```
+
+Start with: [ObjectContent](./ObjectContent.md)
+
+### Nested Path Access
+
+```tsx
+
+
+
+
+
+```
+
+Start with: [Contents](./Contents.md#contentvalue) and [Accessors](./Accessors.md)
+
+### Conditional Rendering
+
+```tsx
+
+
+
+
+
+
+
+```
+
+Start with: [Contents](./Contents.md#nullablecontent)
+
+## Type Safety Limitations
+
+⚠️ Micro-context provides weak type safety. Generic types must be manually specified and cannot be validated across context boundaries. See [MICRO-CONTEXT.md - Weak Type Safety](./MICRO-CONTEXT.md#weak-type-safety) for details.
+
+## Common Pitfalls
+
+### useContent in JSX Children
+
+**Problem:** Calling `useContent()` directly in JSX children runs in the **parent** component's context, not the nested one.
+
+```tsx
+// ✗ Wrong - useContent() runs in parent, gets wrong value
+
+ {useContent() ? 'Yes' : 'No'}
+;
+
+// ✓ Correct - create a separate component
+function BooleanDisplay() {
+ const value = useContent();
+ return <>{value ? 'Yes' : 'No'}>;
+}
+
+
+
+;
+```
+
+**Why?** React evaluates JSX children before passing them to components. The `useContent()` call happens in the parent's render, not inside `FieldContent`.
+
+Read more: [ContentContext - useContent](./ContentContext.md#usecontent)
+
+### DefaultContent with Objects
+
+**Problem:** DefaultContent only works with primitives (string, number, boolean, null, undefined).
+
+```tsx
+// ✗ Error - objects cause React errors
+ {/* Defaults to but elements are objects */}
+
+// ✓ Provide explicit children
+
+
+
+
+
+```
+
+Read more: [Contents - DefaultContent](./Contents.md#defaultcontent)
+
+### Missing Generic Types
+
+**Problem:** Forgetting to add generic types means no autocomplete or validation.
+
+```tsx
+// ✗ No type checking
+
+
+// ✓ Type checking with autocomplete
+ field="name" />
+```
+
+Read more: Each component's Type Safety section
+
+### Path Depth Limitation
+
+**Problem:** Path accessors only support up to 5 levels of nesting.
+
+```tsx
+// ✗ Too deep
+
+
+// ✓ Use function accessor instead
+ obj.a.b.c.d.e.f} />
+```
+
+Read more: [Accessors - Limitations](./Accessors.md#limitations)
+
+## Document Index
+
+- **[MICRO-CONTEXT.md](./MICRO-CONTEXT.md)** - Core pattern explanation
+- **[ContentContext.md](./ContentContext.md)** - ContentProvider, useContent, ContentContext
+- **[Contents.md](./Contents.md)** - ContentValue, FieldContent, NullableContent, DefaultContent
+- **[ArrayContent.md](./ArrayContent.md)** - ArrayContent, IndexContent, IndexContext
+- **[ObjectContent.md](./ObjectContent.md)** - ObjectContent, KeyContent, KeyContext
+- **[Accessors.md](./Accessors.md)** - All accessor functions and types
+
+## Need Help?
+
+1. **Getting Started** → Read [MICRO-CONTEXT.md](./MICRO-CONTEXT.md) first
+2. **Basic Usage** → Check [ContentContext.md](./ContentContext.md) and [Contents.md](./Contents.md)
+3. **Iteration** → See [ArrayContent.md](./ArrayContent.md) or [ObjectContent.md](./ObjectContent.md)
+4. **Advanced Types** → Explore [Accessors.md](./Accessors.md)
+5. **Type Safety Issues** → Review the Type Safety sections in each document
diff --git a/packages/ctablex-core/index.d.ts b/packages/ctablex-core/index.d.ts
index 33b78f8..ad4593e 100644
--- a/packages/ctablex-core/index.d.ts
+++ b/packages/ctablex-core/index.d.ts
@@ -2,39 +2,78 @@ import { Context } from 'react';
import { JSX as JSX_2 } from 'react/jsx-runtime';
import { ReactNode } from 'react';
+/**
+ * Accesses a value using a path string, function, undefined, or null.
+ * - undefined returns the input unchanged
+ * - null returns null
+ * - string uses accessByPath
+ * - function calls the function with the input
+ * @param t - The object to access
+ * @param a - The accessor (path, function, undefined, or null)
+ * @returns The accessed value
+ */
export declare function access>(
t: T,
a: A,
): AccessorValue;
+/**
+ * Accesses a value using a custom extraction function.
+ * @param obj - The object to access
+ * @param fn - Function that extracts the value
+ * @returns The result of calling fn with obj
+ */
export declare function accessByFn>(
obj: T,
fn: F,
): FnAccessorValue;
+/**
+ * Accesses a nested property using a dot-separated string path.
+ * Provides full type safety with autocomplete and compile-time error detection.
+ * @param t - The object to access
+ * @param path - Dot-separated path like "user.address.city"
+ * @returns The value at the specified path
+ */
export declare function accessByPath>(
t: T,
path: K,
): PathAccessorValue;
+/**
+ * Accesses a nested property using a path constrained to return a specific type.
+ * Like accessByPath but only accepts paths that return values of type R.
+ * @param t - The object to access
+ * @param path - Dot-separated path that returns type R
+ * @returns The value at the specified path, typed as R
+ */
export declare function accessByPathTo<
R,
T,
K extends PathAccessorTo = PathAccessorTo,
>(t: T, path: K): R & PathAccessorValue;
+/**
+ * Union type accepting path strings, functions, undefined, or null.
+ */
export declare type Accessor =
| undefined
| null
| PathAccessor
| FnAccessor;
+/**
+ * Union type accepting accessors constrained to return a specific type.
+ */
export declare type AccessorTo =
| undefined
| null
| PathAccessorTo
| FnAccessor;
+/**
+ * The type of the value returned by an accessor.
+ */
export declare type AccessorValue<
T,
A extends Accessor,
@@ -48,6 +87,13 @@ export declare type AccessorValue<
? FnAccessorValue
: never;
+/**
+ * Accesses a value using an accessor constrained to return a specific type.
+ * Like access but only accepts accessors that return values of type R.
+ * @param t - The object to access
+ * @param a - The accessor constrained to return type R
+ * @returns The accessed value, typed as R
+ */
export declare function accessTo<
R,
T,
@@ -63,17 +109,30 @@ declare type AllowedIndexes<
? AllowedIndexes
: Keys;
+/**
+ * Iterates over an array, rendering children for each element.
+ * Provides both the array element via ContentProvider and its index via IndexContext.
+ *
+ * Default children:
+ */
export declare function ArrayContent(
props: ArrayContentProps,
): JSX_2.Element;
export declare interface ArrayContentProps {
+ /** Extracts unique key from each element (path or function). Defaults to index. */
getKey?: PathAccessorTo | ArrayGetKey;
+ /** Content to render for each element. Defaults to . */
children?: ReactNode;
+ /** Content to render between elements (e.g., commas, separators). */
join?: ReactNode;
+ /** Array to iterate. If omitted, uses context value. */
value?: ReadonlyArray;
}
+/**
+ * Function type for extracting a unique key from array elements.
+ */
declare type ArrayGetKey = (value: V, index: number) => string | number;
declare type ComputeRange<
@@ -83,23 +142,49 @@ declare type ComputeRange<
? Result
: ComputeRange;
+/**
+ * The underlying React Context for the micro-context pattern.
+ * @internal This is an internal API and may change in future versions.
+ * Use `ContentProvider` and `useContent` instead.
+ */
export declare const ContentContext: Context<
ContentContextType | undefined
>;
+/**
+ * The type of the content context value.
+ * @internal This is an internal API and may change in future versions.
+ */
export declare type ContentContextType = {
value: V;
};
+/**
+ * Provides a content context that can be retrieved with useContent.
+ * Providers can be nested to create scoped contexts.
+ */
export declare function ContentProvider(
props: ContentProviderProps,
): JSX_2.Element;
+/**
+ * Props for ContentProvider.
+ */
export declare interface ContentProviderProps {
+ /** The value to provide via context. */
value: V;
children?: ReactNode;
}
+/**
+ * Transforms the content value using an accessor, then provides the result to children.
+ * - Path string: Accesses nested properties like "user.address.city"
+ * - Function: Calls the function with the content value
+ * - undefined: Returns the content value unchanged
+ * - null: Returns null
+ *
+ * Default children:
+ */
declare function ContentValue(props: ContentValueProps): JSX_2.Element;
export { ContentValue as AccessorContent };
export { ContentValue };
@@ -112,17 +197,39 @@ declare interface ContentValueProps {
export { ContentValueProps as AccessorContentProps };
export { ContentValueProps };
+/**
+ * Renders primitive values (string, number, null, undefined) directly from context.
+ * Used as the default children for most content components.
+ * Only works with primitives - objects and arrays of objects will cause React errors.
+ */
export declare function DefaultContent(): JSX_2.Element;
+/**
+ * Renders its children only when the content is null, undefined, or empty.
+ * @remarks
+ * Uses {@link useContent} to access the current content from the context.
+ * By default, only arrays with length 0 are considered empty.
+ */
export declare function EmptyContent(
props: EmptyContentProps,
): JSX_2.Element | null;
+/**
+ * Props for the {@link EmptyContent} component.
+ */
export declare interface EmptyContentProps {
+ /** Content to render when the content is empty. */
children?: ReactNode;
+ /** Custom function to determine if content is empty. By default, only arrays with length 0 are considered empty. */
isEmpty?: (content: C) => boolean;
}
+/**
+ * Accesses a single field of an object and provides its value to children.
+ * Simplified version of AccessorContent for object properties.
+ *
+ * Default children:
+ */
export declare function FieldContent(
props: FieldContentProps,
): JSX_2.Element;
@@ -132,8 +239,14 @@ export declare interface FieldContentProps {
children?: ReactNode;
}
+/**
+ * A function that extracts a value from an object.
+ */
export declare type FnAccessor = (t: T) => R;
+/**
+ * The return type of a function accessor.
+ */
export declare type FnAccessorValue> = F extends {
(t: T, ...args: any[]): infer R;
(t: T, ...args: any[]): any;
@@ -178,12 +291,20 @@ export declare type FnAccessorValue> = F extends {
declare type Index40 = ComputeRange<40>[number];
+/**
+ * Displays the current array or object iteration index from IndexContext.
+ * Optionally adds a start offset to the index.
+ */
export declare function IndexContent(props: IndexContentProps): JSX_2.Element;
export declare interface IndexContentProps {
start?: number;
}
+/**
+ * Context providing the current array or object iteration index.
+ * Used internally by ArrayContent and ObjectContent.
+ */
export declare const IndexContext: Context;
declare type IsTuple = T extends readonly any[] & {
@@ -194,19 +315,43 @@ declare type IsTuple = T extends readonly any[] & {
: never
: never;
+/**
+ * Displays the current object property key from KeyContext.
+ */
export declare function KeyContent(): JSX_2.Element;
+/**
+ * Context providing the current object property key during iteration.
+ * Used internally by ObjectContent.
+ */
export declare const KeyContext: Context;
+/**
+ * Renders its children only when the content is not null, not undefined, and not empty.
+ * @remarks
+ * Uses {@link useContent} to access the current content from the context.
+ * By default, only arrays with length 0 are considered empty.
+ */
export declare function NonEmptyContent(
props: NonEmptyContentProps,
): JSX_2.Element | null;
+/**
+ * Props for the {@link NonEmptyContent} component.
+ */
export declare interface NonEmptyContentProps {
+ /** Content to render when the content is not empty. */
children?: ReactNode;
+ /** Custom function to determine if content is empty. By default, only arrays with length 0 are considered empty. */
isEmpty?: (content: C) => boolean;
}
+/**
+ * Conditionally renders content based on whether the value is null or undefined.
+ * Renders nullContent when value is null/undefined, otherwise renders children.
+ *
+ * Default children:
+ */
export declare function NullableContent(
props: NullableContentProps,
): JSX_2.Element;
@@ -216,31 +361,56 @@ export declare interface NullableContentProps {
nullContent?: ReactNode;
}
+/**
+ * Renders its children only when the content is null or undefined.
+ * @remarks
+ * Uses {@link useContent} to access the current content from the context.
+ */
export declare function NullContent(
props: NullContentProps,
): JSX_2.Element | null;
+/**
+ * Props for the {@link NullContent} component.
+ */
export declare interface NullContentProps {
+ /** Content to render when the content is null or undefined. */
children?: ReactNode;
}
+/**
+ * Iterates over object properties, rendering children for each key-value pair.
+ * Provides the property value via ContentProvider, key via KeyContext, and index via IndexContext.
+ */
export declare function ObjectContent(
props: ObjectContentProps,
): JSX_2.Element;
export declare interface ObjectContentProps {
+ /** Generates unique React key for each property. Defaults to property key. */
getKey?: ObjectGetKey;
+ /** Content to render for each property. */
children: ReactNode;
+ /** Content to render between properties (e.g., commas, separators). */
join?: ReactNode;
+ /** Object to iterate. If omitted, uses context value. */
value?: V;
}
+/**
+ * Function type for generating React keys from object properties.
+ */
declare type ObjectGetKey = (
value: V[K],
key: K,
index: number,
) => string | number;
+/**
+ * String literal type representing valid dot-separated paths through an object.
+ * Supports nested properties up to 5 levels deep.
+ * @example "user.address.city"
+ */
export declare type PathAccessor<
T,
TDepth extends any[] = [],
@@ -259,11 +429,18 @@ export declare type PathAccessor<
: never) &
string;
+/**
+ * String literal type representing paths through an object that return a specific type.
+ * Filters PathAccessor to only include paths where the value extends R.
+ */
export declare type PathAccessorTo = {
[K in PathAccessor]: PathAccessorValue extends R ? K : never;
}[PathAccessor] &
string;
+/**
+ * The type of the value at a given path in an object.
+ */
export declare type PathAccessorValue = 0 extends 1 & T
? any
: T extends null | undefined
@@ -284,10 +461,26 @@ declare type PathPrefix<
? `${TPrefix}.${PathAccessor & string}`
: never;
+/**
+ * Retrieves the current value from the nearest ContentProvider.
+ * @param value - Optional override value. If provided, returns this value instead of context.
+ * @returns The content value from context or the override value.
+ * @throws Error if called outside a ContentProvider and no override value is provided.
+ */
export declare function useContent(value?: V): V;
+/**
+ * Retrieves the current iteration index from ArrayContent or ObjectContent.
+ * @returns The zero-based iteration index.
+ * @throws Error if called outside an ArrayContent or ObjectContent.
+ */
export declare function useIndex(): number;
+/**
+ * Retrieves the current object property key from ObjectContent.
+ * @returns The property key (string, number, or symbol).
+ * @throws Error if called outside an ObjectContent.
+ */
export declare function useKey(): string | number | symbol;
export {};
diff --git a/packages/ctablex-core/package.json b/packages/ctablex-core/package.json
index 0e88372..aa7dabe 100644
--- a/packages/ctablex-core/package.json
+++ b/packages/ctablex-core/package.json
@@ -1,11 +1,31 @@
{
"name": "@ctablex/core",
"version": "0.6.5",
+ "description": "Core building blocks for composable, context-based React components using the micro-context pattern",
+ "keywords": [
+ "react",
+ "context",
+ "composition",
+ "micro-context",
+ "declarative",
+ "typescript"
+ ],
+ "homepage": "https://github.com/ctablex/core#readme",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/ctablex/core.git",
+ "directory": "packages/ctablex-core"
+ },
+ "bugs": {
+ "url": "https://github.com/ctablex/core/issues"
+ },
"files": [
"src",
"dist",
"index.d.ts",
- "tsdoc-metadata.json"
+ "tsdoc-metadata.json",
+ "README.md",
+ "docs"
],
"license": "MIT",
"type": "module",
diff --git a/packages/ctablex-core/src/accessor/accessor.ts b/packages/ctablex-core/src/accessor/accessor.ts
index 6971499..1c5e4f9 100644
--- a/packages/ctablex-core/src/accessor/accessor.ts
+++ b/packages/ctablex-core/src/accessor/accessor.ts
@@ -6,12 +6,21 @@ import {
PathAccessorValue,
} from './path-accessor';
+/**
+ * Union type accepting path strings, functions, undefined, or null.
+ */
export type Accessor = undefined | null | PathAccessor | FnAccessor;
+/**
+ * Union type accepting accessors constrained to return a specific type.
+ */
export type AccessorTo =
| undefined
| null
| PathAccessorTo
| FnAccessor;
+/**
+ * The type of the value returned by an accessor.
+ */
export type AccessorValue> = A extends undefined
? T
: A extends null
@@ -22,6 +31,16 @@ export type AccessorValue> = A extends undefined
? FnAccessorValue
: never;
+/**
+ * Accesses a value using a path string, function, undefined, or null.
+ * - undefined returns the input unchanged
+ * - null returns null
+ * - string uses accessByPath
+ * - function calls the function with the input
+ * @param t - The object to access
+ * @param a - The accessor (path, function, undefined, or null)
+ * @returns The accessed value
+ */
export function access>(
t: T,
a: A,
@@ -41,6 +60,13 @@ export function access>(
return a(t);
}
+/**
+ * Accesses a value using an accessor constrained to return a specific type.
+ * Like access but only accepts accessors that return values of type R.
+ * @param t - The object to access
+ * @param a - The accessor constrained to return type R
+ * @returns The accessed value, typed as R
+ */
export function accessTo = AccessorTo>(
t: T,
a: A,
diff --git a/packages/ctablex-core/src/accessor/fn-accessor.ts b/packages/ctablex-core/src/accessor/fn-accessor.ts
index 496c909..8660eff 100644
--- a/packages/ctablex-core/src/accessor/fn-accessor.ts
+++ b/packages/ctablex-core/src/accessor/fn-accessor.ts
@@ -1,4 +1,10 @@
+/**
+ * A function that extracts a value from an object.
+ */
export type FnAccessor = (t: T) => R;
+/**
+ * The return type of a function accessor.
+ */
export type FnAccessorValue> = F extends {
(t: T, ...args: any[]): infer R;
(t: T, ...args: any[]): any;
@@ -41,6 +47,12 @@ export type FnAccessorValue> = F extends {
? R
: any;
+/**
+ * Accesses a value using a custom extraction function.
+ * @param obj - The object to access
+ * @param fn - Function that extracts the value
+ * @returns The result of calling fn with obj
+ */
export function accessByFn>(
obj: T,
fn: F,
diff --git a/packages/ctablex-core/src/accessor/path-accessor.ts b/packages/ctablex-core/src/accessor/path-accessor.ts
index bdd548f..8919dae 100644
--- a/packages/ctablex-core/src/accessor/path-accessor.ts
+++ b/packages/ctablex-core/src/accessor/path-accessor.ts
@@ -23,6 +23,11 @@ type AllowedIndexes<
Tuple extends readonly [infer _, ...infer Tail]
? AllowedIndexes
: Keys;
+/**
+ * String literal type representing valid dot-separated paths through an object.
+ * Supports nested properties up to 5 levels deep.
+ * @example "user.address.city"
+ */
export type PathAccessor<
T,
TDepth extends any[] = [],
@@ -46,6 +51,9 @@ type PathPrefix = TPrefix extends keyof T &
? `${TPrefix}.${PathAccessor & string}`
: never;
+/**
+ * The type of the value at a given path in an object.
+ */
export type PathAccessorValue = 0 extends 1 & T
? any
: T extends null | undefined
@@ -58,11 +66,22 @@ export type PathAccessorValue = 0 extends 1 & T
: undefined
: never;
+/**
+ * String literal type representing paths through an object that return a specific type.
+ * Filters PathAccessor to only include paths where the value extends R.
+ */
export type PathAccessorTo = {
[K in PathAccessor]: PathAccessorValue extends R ? K : never;
}[PathAccessor] &
string;
+/**
+ * Accesses a nested property using a dot-separated string path.
+ * Provides full type safety with autocomplete and compile-time error detection.
+ * @param t - The object to access
+ * @param path - Dot-separated path like "user.address.city"
+ * @returns The value at the specified path
+ */
export function accessByPath>(
t: T,
path: K,
@@ -71,6 +90,13 @@ export function accessByPath>(
return path.split('.').reduce((acc, key) => acc?.[key], t);
}
+/**
+ * Accesses a nested property using a path constrained to return a specific type.
+ * Like accessByPath but only accepts paths that return values of type R.
+ * @param t - The object to access
+ * @param path - Dot-separated path that returns type R
+ * @returns The value at the specified path, typed as R
+ */
export function accessByPathTo<
R,
T,
diff --git a/packages/ctablex-core/src/content-provider.tsx b/packages/ctablex-core/src/content-provider.tsx
index 0fe5614..104c30f 100644
--- a/packages/ctablex-core/src/content-provider.tsx
+++ b/packages/ctablex-core/src/content-provider.tsx
@@ -1,6 +1,12 @@
import { ReactNode, useContext, useMemo } from 'react';
import { ContentContext } from './contexts/content-context';
+/**
+ * Retrieves the current value from the nearest ContentProvider.
+ * @param value - Optional override value. If provided, returns this value instead of context.
+ * @returns The content value from context or the override value.
+ * @throws Error if called outside a ContentProvider and no override value is provided.
+ */
export function useContent(value?: V) {
const context = useContext(ContentContext);
if (value !== undefined) {
@@ -12,10 +18,19 @@ export function useContent(value?: V) {
return context.value as V;
}
+/**
+ * Props for ContentProvider.
+ */
export interface ContentProviderProps {
+ /** The value to provide via context. */
value: V;
children?: ReactNode;
}
+
+/**
+ * Provides a content context that can be retrieved with useContent.
+ * Providers can be nested to create scoped contexts.
+ */
export function ContentProvider(props: ContentProviderProps) {
const context = useMemo(() => ({ value: props.value }), [props.value]);
return (
diff --git a/packages/ctablex-core/src/contents/array-content.tsx b/packages/ctablex-core/src/contents/array-content.tsx
index 52acc6e..94ec4ce 100644
--- a/packages/ctablex-core/src/contents/array-content.tsx
+++ b/packages/ctablex-core/src/contents/array-content.tsx
@@ -4,17 +4,30 @@ import { ContentProvider, useContent } from '../content-provider';
import { IndexContext } from '../contexts/index-context';
import { DefaultContent } from './default-content';
+/**
+ * Function type for extracting a unique key from array elements.
+ */
export type ArrayGetKey = (value: V, index: number) => string | number;
export interface ArrayContentProps {
+ /** Extracts unique key from each element (path or function). Defaults to index. */
getKey?: PathAccessorTo | ArrayGetKey;
+ /** Content to render for each element. Defaults to . */
children?: ReactNode;
+ /** Content to render between elements (e.g., commas, separators). */
join?: ReactNode;
+ /** Array to iterate. If omitted, uses context value. */
value?: ReadonlyArray;
}
const defaultChildren = ;
+/**
+ * Iterates over an array, rendering children for each element.
+ * Provides both the array element via ContentProvider and its index via IndexContext.
+ *
+ * Default children:
+ */
export function ArrayContent(props: ArrayContentProps) {
const {
getKey: getKeyProps,
diff --git a/packages/ctablex-core/src/contents/content-value.tsx b/packages/ctablex-core/src/contents/content-value.tsx
index a8edda3..fd50252 100644
--- a/packages/ctablex-core/src/contents/content-value.tsx
+++ b/packages/ctablex-core/src/contents/content-value.tsx
@@ -10,6 +10,15 @@ export interface ContentValueProps {
}
const defaultChildren = ;
+/**
+ * Transforms the content value using an accessor, then provides the result to children.
+ * - Path string: Accesses nested properties like "user.address.city"
+ * - Function: Calls the function with the content value
+ * - undefined: Returns the content value unchanged
+ * - null: Returns null
+ *
+ * Default children:
+ */
export function ContentValue(props: ContentValueProps) {
const { accessor, children = defaultChildren } = props;
const content = useContent(props.value);
diff --git a/packages/ctablex-core/src/contents/default-content.tsx b/packages/ctablex-core/src/contents/default-content.tsx
index e0e52a2..a94a49b 100644
--- a/packages/ctablex-core/src/contents/default-content.tsx
+++ b/packages/ctablex-core/src/contents/default-content.tsx
@@ -1,5 +1,10 @@
import { useContent } from '../content-provider';
+/**
+ * Renders primitive values (string, number, null, undefined) directly from context.
+ * Used as the default children for most content components.
+ * Only works with primitives - objects and arrays of objects will cause React errors.
+ */
export function DefaultContent() {
const content = useContent();
return <>{content}>;
diff --git a/packages/ctablex-core/src/contents/empty-content.tsx b/packages/ctablex-core/src/contents/empty-content.tsx
index 986d8e5..ff68a78 100644
--- a/packages/ctablex-core/src/contents/empty-content.tsx
+++ b/packages/ctablex-core/src/contents/empty-content.tsx
@@ -1,15 +1,31 @@
import { ReactNode } from 'react';
import { useContent } from '../content-provider';
+/**
+ * Props for the {@link EmptyContent} component.
+ */
export interface EmptyContentProps {
+ /** Content to render when the content is empty. */
children?: ReactNode;
+ /** Custom function to determine if content is empty. By default, only arrays with length 0 are considered empty. */
isEmpty?: (content: C) => boolean;
}
+/**
+ * Default implementation to check if content is empty.
+ * @param content - The content to check
+ * @returns `true` if content is an array with length 0, `false` otherwise
+ */
export function defaultIsEmpty(content: C): boolean {
return Array.isArray(content) && content.length === 0;
}
+/**
+ * Renders its children only when the content is null, undefined, or empty.
+ * @remarks
+ * Uses {@link useContent} to access the current content from the context.
+ * By default, only arrays with length 0 are considered empty.
+ */
export function EmptyContent(props: EmptyContentProps) {
const { children, isEmpty = defaultIsEmpty } = props;
const content = useContent();
diff --git a/packages/ctablex-core/src/contents/field-content.tsx b/packages/ctablex-core/src/contents/field-content.tsx
index 02dd2ee..19f7098 100644
--- a/packages/ctablex-core/src/contents/field-content.tsx
+++ b/packages/ctablex-core/src/contents/field-content.tsx
@@ -8,6 +8,12 @@ export interface FieldContentProps {
}
const defaultChildren = ;
+/**
+ * Accesses a single field of an object and provides its value to children.
+ * Simplified version of AccessorContent for object properties.
+ *
+ * Default children:
+ */
export function FieldContent(props: FieldContentProps) {
const { field, children = defaultChildren } = props;
const content = useContent();
diff --git a/packages/ctablex-core/src/contents/index-content.tsx b/packages/ctablex-core/src/contents/index-content.tsx
index 1dbd147..bb10c02 100644
--- a/packages/ctablex-core/src/contents/index-content.tsx
+++ b/packages/ctablex-core/src/contents/index-content.tsx
@@ -3,6 +3,10 @@ import { useIndex } from '../contexts/index-context';
export interface IndexContentProps {
start?: number;
}
+/**
+ * Displays the current array or object iteration index from IndexContext.
+ * Optionally adds a start offset to the index.
+ */
export function IndexContent(props: IndexContentProps) {
const { start = 0 } = props;
const index = useIndex() + start;
diff --git a/packages/ctablex-core/src/contents/key-content.tsx b/packages/ctablex-core/src/contents/key-content.tsx
index 7b4308c..e677eb9 100644
--- a/packages/ctablex-core/src/contents/key-content.tsx
+++ b/packages/ctablex-core/src/contents/key-content.tsx
@@ -1,5 +1,8 @@
import { useKey } from '../contexts/key-context';
+/**
+ * Displays the current object property key from KeyContext.
+ */
export function KeyContent() {
const key = useKey();
return <>{key}>;
diff --git a/packages/ctablex-core/src/contents/non-empty-content.tsx b/packages/ctablex-core/src/contents/non-empty-content.tsx
index bf72dbf..9f90cb4 100644
--- a/packages/ctablex-core/src/contents/non-empty-content.tsx
+++ b/packages/ctablex-core/src/contents/non-empty-content.tsx
@@ -2,11 +2,22 @@ import { ReactNode } from 'react';
import { useContent } from '../content-provider';
import { defaultIsEmpty } from './empty-content';
+/**
+ * Props for the {@link NonEmptyContent} component.
+ */
export interface NonEmptyContentProps {
+ /** Content to render when the content is not empty. */
children?: ReactNode;
+ /** Custom function to determine if content is empty. By default, only arrays with length 0 are considered empty. */
isEmpty?: (content: C) => boolean;
}
+/**
+ * Renders its children only when the content is not null, not undefined, and not empty.
+ * @remarks
+ * Uses {@link useContent} to access the current content from the context.
+ * By default, only arrays with length 0 are considered empty.
+ */
export function NonEmptyContent(props: NonEmptyContentProps) {
const { children, isEmpty = defaultIsEmpty } = props;
const content = useContent();
diff --git a/packages/ctablex-core/src/contents/null-content.tsx b/packages/ctablex-core/src/contents/null-content.tsx
index 99e1091..8f560a1 100644
--- a/packages/ctablex-core/src/contents/null-content.tsx
+++ b/packages/ctablex-core/src/contents/null-content.tsx
@@ -1,10 +1,19 @@
import { ReactNode } from 'react';
import { useContent } from '../content-provider';
+/**
+ * Props for the {@link NullContent} component.
+ */
export interface NullContentProps {
+ /** Content to render when the content is null or undefined. */
children?: ReactNode;
}
+/**
+ * Renders its children only when the content is null or undefined.
+ * @remarks
+ * Uses {@link useContent} to access the current content from the context.
+ */
export function NullContent(props: NullContentProps) {
const { children } = props;
const content = useContent();
diff --git a/packages/ctablex-core/src/contents/nullable-content.tsx b/packages/ctablex-core/src/contents/nullable-content.tsx
index 3f0f4e4..dff8008 100644
--- a/packages/ctablex-core/src/contents/nullable-content.tsx
+++ b/packages/ctablex-core/src/contents/nullable-content.tsx
@@ -8,6 +8,12 @@ export interface NullableContentProps {
}
const defaultChildren = ;
+/**
+ * Conditionally renders content based on whether the value is null or undefined.
+ * Renders nullContent when value is null/undefined, otherwise renders children.
+ *
+ * Default children:
+ */
export function NullableContent(props: NullableContentProps) {
const { nullContent = null, children = defaultChildren } = props;
const content = useContent();
diff --git a/packages/ctablex-core/src/contents/object-content.tsx b/packages/ctablex-core/src/contents/object-content.tsx
index 8ff2156..becdd08 100644
--- a/packages/ctablex-core/src/contents/object-content.tsx
+++ b/packages/ctablex-core/src/contents/object-content.tsx
@@ -3,6 +3,9 @@ import { ContentProvider, useContent } from '../content-provider';
import { IndexContext } from '../contexts/index-context';
import { KeyContext } from '../contexts/key-context';
+/**
+ * Function type for generating React keys from object properties.
+ */
export type ObjectGetKey = (
value: V[K],
key: K,
@@ -10,14 +13,22 @@ export type ObjectGetKey = (
) => string | number;
export interface ObjectContentProps {
+ /** Generates unique React key for each property. Defaults to property key. */
getKey?: ObjectGetKey;
+ /** Content to render for each property. */
children: ReactNode;
+ /** Content to render between properties (e.g., commas, separators). */
join?: ReactNode;
+ /** Object to iterate. If omitted, uses context value. */
value?: V;
}
const defaultGetKey: ObjectGetKey = (value, key, index) => key.toString();
+/**
+ * Iterates over object properties, rendering children for each key-value pair.
+ * Provides the property value via ContentProvider, key via KeyContext, and index via IndexContext.
+ */
export function ObjectContent(props: ObjectContentProps) {
const { getKey = defaultGetKey, children, join = null } = props;
const content = useContent(props.value);
diff --git a/packages/ctablex-core/src/contexts/content-context.tsx b/packages/ctablex-core/src/contexts/content-context.tsx
index 547d4cb..5bc208e 100644
--- a/packages/ctablex-core/src/contexts/content-context.tsx
+++ b/packages/ctablex-core/src/contexts/content-context.tsx
@@ -1,6 +1,16 @@
import { createContext } from 'react';
+/**
+ * The type of the content context value.
+ * @internal This is an internal API and may change in future versions.
+ */
export type ContentContextType = { value: V };
+
+/**
+ * The underlying React Context for the micro-context pattern.
+ * @internal This is an internal API and may change in future versions.
+ * Use `ContentProvider` and `useContent` instead.
+ */
export const ContentContext = createContext<
ContentContextType | undefined
>(undefined);
diff --git a/packages/ctablex-core/src/contexts/index-context.tsx b/packages/ctablex-core/src/contexts/index-context.tsx
index b5f829a..267615c 100644
--- a/packages/ctablex-core/src/contexts/index-context.tsx
+++ b/packages/ctablex-core/src/contexts/index-context.tsx
@@ -1,7 +1,16 @@
import { createContext, useContext } from 'react';
+/**
+ * Context providing the current array or object iteration index.
+ * Used internally by ArrayContent and ObjectContent.
+ */
export const IndexContext = createContext(undefined);
+/**
+ * Retrieves the current iteration index from ArrayContent or ObjectContent.
+ * @returns The zero-based iteration index.
+ * @throws Error if called outside an ArrayContent or ObjectContent.
+ */
export function useIndex() {
const context = useContext(IndexContext);
if (context === undefined) {
diff --git a/packages/ctablex-core/src/contexts/key-context.tsx b/packages/ctablex-core/src/contexts/key-context.tsx
index 422f985..b63b7e6 100644
--- a/packages/ctablex-core/src/contexts/key-context.tsx
+++ b/packages/ctablex-core/src/contexts/key-context.tsx
@@ -1,9 +1,18 @@
import { createContext, useContext } from 'react';
+/**
+ * Context providing the current object property key during iteration.
+ * Used internally by ObjectContent.
+ */
export const KeyContext = createContext(
undefined,
);
+/**
+ * Retrieves the current object property key from ObjectContent.
+ * @returns The property key (string, number, or symbol).
+ * @throws Error if called outside an ObjectContent.
+ */
export function useKey() {
const context = useContext(KeyContext);
if (context === undefined) {