diff --git a/docs-site/src/content/docs/api/createComponent.md b/docs-site/src/content/docs/api/createComponent.md
index b47d610..b5ef4b0 100644
--- a/docs-site/src/content/docs/api/createComponent.md
+++ b/docs-site/src/content/docs/api/createComponent.md
@@ -22,7 +22,7 @@ To create a Regor component, call the `createComponent` function with the follow
- `options` (optional): An array of strings that defines component properties or an object that allows you to configure various options for the component, such as whether to use interpolation, props, the component's default name, or the component context.
- `context` (optional): A function that defines the Regor context for the component. This function receives a `ComponentHead` object, which you can use to specify the component's behavior and props. It should return the Regor context.
- - `head.autoProps`: Automatically assigns properties defined in the `:props` binding to the component context. Defaults to `true`.
+ - `head.autoProps`: Automatically assigns properties defined in the `:context` binding to the component context. Defaults to `true`.
- `head.entangle`: Keeps refs defined in the component context entangled with `head.props` refs. Defaults to `true`.
- `head.enableSwitch`: Enables slot context switching to the parent. Defaults to `false`.
- `head.onAutoPropsAssigned`: Callback invoked after auto props get assigned to the component context.
@@ -94,3 +94,4 @@ The `createComponent` function returns a component object with the following pro
- [`toJsonTemplate`](/api/toJsonTemplate)
[Back to the API list](/api/)
+
diff --git a/docs-site/src/content/docs/api/index.md b/docs-site/src/content/docs/api/index.md
index e3ba0f3..4864e9c 100644
--- a/docs-site/src/content/docs/api/index.md
+++ b/docs-site/src/content/docs/api/index.md
@@ -1,75 +1,83 @@
---
title: Regor API
sidebar:
- order: 5
+ order: 6
---
-In this section, you will find a list of Regor's API methods organized by categories. Click on each method to view its detailed documentation.
+This section contains the function-level reference for Regor.
-**App / Component Template Functions:**
+Use this page as an index, then open each API page for signature details and examples.
-- [`createApp`](/api/createApp)
-- [`createComponent`](/api/createComponent)
-- [`toFragment`](/api/toFragment)
-- [`toJsonTemplate`](/api/toJsonTemplate)
-- [`RegorConfig`](/api/regorConfig)
+## App and Components
-**Cleanup Functions:**
+1. [`createApp`](./createApp)
+2. [`createComponent`](./createComponent)
+3. [`toFragment`](./toFragment)
+4. [`toJsonTemplate`](./toJsonTemplate)
+5. [`RegorConfig`](./regorConfig)
-- [`addUnbinder`](/api/addUnbinder)
-- [`getBindData`](/api/getBindData)
-- [`removeNode`](/api/removeNode)
-- [`unbind`](/api/unbind)
+## Reactivity
-**Compute Functions:**
+1. [`ref`](./ref)
+2. [`sref`](./sref)
+3. [`isRef`](./isRef)
+4. [`isDeepRef`](./isDeepRef)
+5. [`unref`](./unref)
+6. [`pause`](./pause)
+7. [`resume`](./resume)
+8. [`trigger`](./trigger)
+9. [`entangle`](./entangle)
-- [`computed`](/api/computed)
-- [`computeRef`](/api/computeRef)
-- [`computeMany`](/api/computeMany)
-- [`watchEffect`](/api/watchEffect)
-- [`collectRefs`](/api/collectRefs)
-- [`silence`](/api/silence)
+## Computed and Effects
-**Misc Functions:**
+1. [`computed`](./computed)
+2. [`computeRef`](./computeRef)
+3. [`computeMany`](./computeMany)
+4. [`watchEffect`](./watchEffect)
+5. [`collectRefs`](./collectRefs)
+6. [`silence`](./silence)
-- [`flatten`](/api/flatten)
-- [`isRaw`](/api/isRaw)
-- [`markRaw`](/api/markRaw)
-- [`persist`](/api/persist)
-- [`html`](/api/html)
-- [`raw`](/api/raw)
+## Observation and Batch
-**Observe Functions:**
+1. [`observe`](./observe)
+2. [`observeMany`](./observeMany)
+3. [`observerCount`](./observerCount)
+4. [`batch`](./batch)
+5. [`startBatch`](./startBatch)
+6. [`endBatch`](./endBatch)
-- [`observe`](/api/observe)
-- [`observeMany`](/api/observeMany)
-- [`observerCount`](/api/observerCount)
-- [`batch`](/api/batch)
-- [`startBatch`](/api/startBatch)
-- [`endBatch`](/api/endBatch)
+## Lifecycle and Scope
-**Reactivity Functions:**
+1. [`useScope`](./useScope)
+2. [`onMounted`](./onMounted)
+3. [`onUnmounted`](./onUnmounted)
-- [`ref`](/api/ref)
-- [`sref`](/api/sref)
-- [`isDeepRef`](/api/isDeepRef)
-- [`isRef`](/api/isRef)
-- [`pause`](/api/pause)
-- [`resume`](/api/resume)
-- [`trigger`](/api/trigger)
-- [`unref`](/api/unref)
-- [`entangle`](/api/entangle)
+## Cleanup and Unbind
-**Composition Functions:**
+1. [`addUnbinder`](./addUnbinder)
+2. [`getBindData`](./getBindData)
+3. [`removeNode`](./removeNode)
+4. [`unbind`](./unbind)
-- [`useScope`](/api/useScope)
-- [`onMounted`](/api/onMounted)
-- [`onUnmounted`](/api/onUnmounted)
+## Utilities
-**Log Configuration:**
+1. [`flatten`](./flatten)
+2. [`markRaw`](./markRaw)
+3. [`isRaw`](./isRaw)
+4. [`persist`](./persist)
+5. [`html`](./html)
+6. [`raw`](./raw)
-- [`warningHandler`](/api/warningHandler)
+## Logging
-Each link will take you to a dedicated documentation page for the respective method, where you can find detailed information, examples, and usage guidelines.
+1. [`warningHandler`](./warningHandler)
-[Back to the main](/)
+## Recommendation
+
+Start with:
+
+1. [`createApp`](./createApp)
+2. [`ref`](./ref)
+3. [`sref`](./sref)
+4. [`computed`](./computed)
+5. [`watchEffect`](./watchEffect)
diff --git a/docs-site/src/content/docs/directives/context.md b/docs-site/src/content/docs/directives/context.md
new file mode 100644
index 0000000..246bf6b
--- /dev/null
+++ b/docs-site/src/content/docs/directives/context.md
@@ -0,0 +1,54 @@
+---
+title: :context Directive
+---
+
+`:context` assigns an object into a component instance context, reactively.
+`r-context` is an alias with the same behavior.
+
+Use it when you want to pass multiple values to a component in one place, including fields that are not declared in `props`.
+
+## What it does
+
+```html
+
| ` / ` | ` may be rewritten to alias hosts (`trx` / `tdx` / `thx`) with `is="r-*"` markers.
+2. In table-sensitive positions:
+ - non-native direct children may be rewritten to safe hosts with `is="regor:OriginalTag"`.
+3. Self-closing custom tags under row context are normalized to explicit open/close tags.
+
+This avoids browser table parser dropping/reparenting custom nodes.
+
+## Where Expressions Are Used
+
+Typical expression points:
+
+1. Text/content: `r-text`, interpolation
+2. Attributes/properties: `:x`, `.x`, `r-bind:x`
+3. Events: `@click`, `r-on:*`
+4. Control flow: `r-if`, `r-else-if`, `r-for`
+5. Styling: `:class`, `:style`
+6. Components: `:is`, `:context`, `r-context`
+
+## Expression Scope and Resolution
+
+Expressions are evaluated against active context stack:
+
+1. Current app/component context
+2. Loop aliases (`r-for`)
+3. Slot switch contexts (when `head.enableSwitch = true`)
+4. Global context from `RegorConfig`
+
+Special identifiers:
+
+1. `$root`: root-most context
+2. `$parent`: immediate parent context
+3. `$ctx`: full context stack array
+4. `this`: current context object
+5. `$event`: available in event/lazy evaluation paths
+
+Regor auto-unrefs refs in expressions, so template code uses `user.name`, not `user.name()` / `.value`.
+
+## Supported Expression Syntax (Runtime)
+
+Backed by Regor’s jsep/regorEval pipeline, templates support:
+
+1. Member access, optional chaining: `a.b`, `a?.b`, `a[k]`
+2. Calls/new: `fn(x)`, `new Date(0)`
+3. Arithmetic/comparison/logical/bitwise operators
+4. Nullish coalescing / ternary: `a ?? b`, `ok ? a : b`
+5. Assignments and updates: `x = 1`, `x += 2`, `x++`, `--x`
+6. Arrays/objects/spread/computed object keys
+7. Arrow functions
+8. Template literals and tagged templates
+9. Regex literals
+10. Comma/compound forms
+
+## `r-for` Key Notes
+
+Both forms are supported for expression-based keys:
+
+1. `key="row.id"`
+2. `:key="row.id"`
+
+Nested paths such as `a.b.c.d` are supported.
+Use stable domain IDs for predictable updates.
+
+## Practical Patterns
+
+1. Prefer `r-text` for hot repeated rows.
+2. Keep template expressions lightweight; move heavy logic into methods/computed refs.
+3. Use stable keys in lists.
+4. Use `r-pre` to protect literal template snippets from interpolation transform.
+
+## Advanced Samples
+
+### 1) Static-first page + dynamic island
+
+Use existing server-rendered markup and bind only the dynamic area.
+
+```html
+My Shop+
+
+ Welcome, {{ userName }}
+
+
{{ parentMessage }} +{{ msg }}' },
+ cfg,
+)
+```
+
+or local skip:
+
+```html
+
+ {{ this stays literal }}
+
+```
+
+Use these when docs/snippets must display `{{ ... }}` without binding.
+
+## See Also
+
+1. [Directives](../directives)
+2. [Reactivity](./reactivity)
+3. [Components](./components)
+4. [r-for](../directives/r-for)
diff --git a/docs-site/src/content/docs/guide/troubleshooting.md b/docs-site/src/content/docs/guide/troubleshooting.md
new file mode 100644
index 0000000..a712394
--- /dev/null
+++ b/docs-site/src/content/docs/guide/troubleshooting.md
@@ -0,0 +1,180 @@
+---
+title: Troubleshooting
+sidebar:
+ order: 90
+---
+
+Use this page as a symptom -> cause -> fix reference based on current Regor runtime behavior.
+
+## Quick Triage
+
+Before deep debugging:
+
+1. Verify root exists and app actually mounted.
+2. Verify template is the one you think is mounted (selector/element/string/json path).
+3. Verify binding channel is correct (`:x` vs `:context` vs `r-bind="{...}"`).
+4. Verify scope/hook usage (`useScope` for composables).
+5. Verify teardown timing (`unmount` + `drainUnbind` in tests).
+
+## App Does Not Mount
+
+Symptom:
+
+1. `createApp` throws and nothing renders.
+
+Likely cause:
+
+1. Missing root element.
+
+Runtime error:
+
+1. `createApp can't find root element...`
+
+Fix:
+
+```ts
+createApp(ctx, { element: document.querySelector('#app')! })
+// or
+createApp(ctx, { selector: '#app' })
+```
+
+If you use `createApp(ctx, '... ')`, Regor expects a real `#app` in DOM.
+
+## `onMounted` / `onUnmounted` Throws
+
+Symptom:
+
+1. Error about composables requiring scope.
+
+Likely cause:
+
+1. Hook registered outside active scope.
+
+Runtime error:
+
+1. `Use composables in scope. usage: useScope(() => new MyApp())`
+
+Fix:
+
+```ts
+createApp(
+ useScope(() => {
+ onMounted(() => {})
+ onUnmounted(() => {})
+ return { count: ref(0) }
+ }),
+ { element: root },
+)
+```
+
+## Interpolation Not Updating
+
+Symptom:
+
+1. `{{ expr }}` / `[[ expr ]]` appears as raw text.
+
+Likely causes:
+
+1. `config.useInterpolation = false`
+2. Markup is under `r-pre`
+3. Expression resolves to missing path/value
+
+Fix checklist:
+
+1. Enable interpolation (`RegorConfig.useInterpolation = true`) when needed.
+2. Remove `r-pre` from dynamic subtree.
+3. Confirm expression resolves in active context.
+
+## Component Inputs Not Reaching Context
+
+Most confusion here comes from input channel mismatch.
+
+Correct routing:
+
+1. `:x` / `.x` / `r-bind:x`:
+ works as component prop only when `x` is declared in `props`.
+2. `:context` / `r-context`:
+ object-style component input channel.
+3. `r-bind="{...}"` (object form):
+ attribute channel on component hosts (fallthrough), not `head.props` object assignment.
+
+If component sets `head.autoProps = false`, you must map `head.props` manually in `context(head)`.
+
+## Parent-Child Ref Sync Feels Wrong
+
+Check `head.entangle` semantics:
+
+1. `head.entangle = true`:
+ ref-to-ref inputs are two-way entangled.
+2. `head.entangle = false`:
+ ref-to-ref inputs are snapshot-only (no persistent sync).
+
+Also check whether parent sends ref source vs primitive expression:
+
+1. `:context="{ x: someRef }"` behaves differently from `:context="{ x: someRef + 1 }"`.
+
+## Slot Expression Uses Wrong Context
+
+Symptom:
+
+1. Slot content appears to evaluate against child context instead of parent.
+
+Fix:
+
+1. Enable slot context switching in component:
+
+```ts
+context: (head) => {
+ head.enableSwitch = true
+ return {}
+}
+```
+
+Then verify slot naming matches (`name` / `#name`).
+
+## `r-for` Updates Look Broken
+
+Checklist:
+
+1. Use stable identity keys (`:key="row.id"` or `key="row.id"`).
+2. Ensure key is unique and stable across updates.
+3. Avoid mutating key identity unexpectedly between renders.
+
+## Cleanup / Memory / Test Flakiness
+
+Know the teardown difference:
+
+1. `app.unmount()` removes root node and queues unbind flush.
+2. `app.unbind()` detaches bindings without removing DOM.
+
+In tests, if assertions depend on cleanup side effects:
+
+```ts
+app.unmount()
+await drainUnbind()
+```
+
+## Warnings You Should Not Ignore
+
+Frequent warning types and meaning:
+
+1. Missing binding expression:
+ directive attribute exists but expression is empty.
+2. Invalid `r-for` expression:
+ loop syntax is malformed.
+3. Binding requires object expression:
+ directive expected object input (for example object-style binding paths).
+4. Key is empty:
+ dynamic key path resolved to empty key.
+5. Model requires ref / unsupported element:
+ `r-model` target is invalid for current element/source.
+
+## When Reporting an Issue
+
+Include:
+
+1. Minimal template + context snippet.
+2. Expected result vs actual result.
+3. Which channel you used (`:x`, `r-bind:x`, `:context`, `r-context`, `r-bind="{...}"`).
+4. Whether problem persists after `unmount` + `await drainUnbind()`.
+5. Regor version + runtime environment.
diff --git a/docs-site/src/content/docs/guide/typescript.md b/docs-site/src/content/docs/guide/typescript.md
new file mode 100644
index 0000000..abb18ab
--- /dev/null
+++ b/docs-site/src/content/docs/guide/typescript.md
@@ -0,0 +1,110 @@
+---
+title: TypeScript
+sidebar:
+ order: 40
+---
+
+Regor has strong TypeScript support across app context, component context, and component head typing.
+
+## Why this matters
+
+You can model app and component state with real TypeScript types (interfaces/classes), and keep runtime template syntax concise.
+
+In template expressions, Regor resolves refs automatically, so you write `user.name` instead of `user.name.value` or `user.name()`.
+
+## Typed app context with interfaces
+
+```ts
+import { createApp, html, type IRegorContext, type Ref, ref } from 'regor'
+
+interface TodoItem {
+ id: number
+ label: string
+}
+
+interface MyApp extends IRegorContext {
+ title: Ref{{ query }} - {{ count }} `, + }, +) +``` + +## Typed component props and context with `ComponentHead... ', { defaultName: 'CardView' })
+ * cfg.addComponent(Card)
+ * // usable as
- prop-1-prop-2
+ My Property 1-prop-2
ok-prop-2
@@ -67,14 +69,14 @@ test('should render components with reactive properties', () => {
prop-1-prop-2
-
+
ok-ok
ok-ok
-
+
prop-1-prop-2
@@ -85,7 +87,7 @@ test('should render components with reactive properties', () => {
root.innerHTML,
html`
- prop-1-prop-2
+ My Property 1-prop-2
yes-prop-2
@@ -96,14 +98,14 @@ test('should render components with reactive properties', () => {
prop-1-prop-2
-
+
yes-yes
yes-yes
-
+
prop-1-prop-2
@@ -148,6 +150,304 @@ test('should resolve camelCase dynamic prop bindings on components', () => {
)
})
+test('component binding matrix: declared props, undeclared attrs, :context, and r-bind object', () => {
+ const root = document.createElement('div')
+
+ const demoComponent = createComponent(
+ html`{{ title }}+{{ extra }} +{{ declaredOnly }} +
+ {{ title }}
+ {{ extra }}
+ `,
+ {
+ context: () => ({
+ title: ref('title-default'),
+ extra: ref('extra-default'),
+ }),
+ },
+ )
+
+ const app = createApp(
+ {
+ components: { myComponent },
+ titleSrc: ref('title-1'),
+ extraSrc: ref('extra-1'),
+ },
+ {
+ element: root,
+ template: html`
+ {{ canEdit ? 'edit' : 'view' }}
+ {{ accessLevel }}
+ `,
+ {
+ context: () => ({
+ canEdit: ref(false),
+ accessLevel: ref('guest'),
+ }),
+ },
+ )
+
+ const app = createApp(
+ {
+ components: { card },
+ flag: ref(true),
+ level: ref('admin'),
+ },
+ {
+ element: root,
+ template: html`
+ {{ title }}
+ {{ note }}
+ `,
+ {
+ props: ['title'],
+ context: (head) => ({
+ title: ref(head.props.title ?? 'title-default'),
+ note: ref('note-default'),
+ }),
+ },
+ )
+
+ const app = createApp(
+ {
+ components: { card },
+ titleSrc: ref('hello'),
+ noteSrc: ref('n1'),
+ },
+ {
+ element: root,
+ template: html`
+ {{ live }}
+ {{ once }}
+ `,
+ {
+ context: () => ({
+ live: ref(0),
+ once: ref(0),
+ }),
+ },
+ )
+
+ const app = createApp(
+ {
+ components: { comp },
+ count: ref(1),
+ },
+ {
+ element: root,
+ template: html`
+ `,
+ },
+ )
+
+ expect(root.querySelector('.no-switch')?.textContent?.trim()).toBe(
+ 'from-component-no-switch',
+ )
+ expect(root.querySelector('.with-switch')?.textContent?.trim()).toBe(
+ 'from-parent',
+ )
+
+ app.context.message('from-parent-next')
+ expect(root.querySelector('.no-switch')?.textContent?.trim()).toBe(
+ 'from-component-no-switch',
+ )
+ expect(root.querySelector('.with-switch')?.textContent?.trim()).toBe(
+ 'from-parent-next',
+ )
+})
+
test('createComponent supports disabling interpolation and direct element children scan', () => {
const rootEl = document.createElement('div')
rootEl.innerHTML = '{{ message }} +{{ message }} +{{ msg }} ' @@ -673,8 +1031,7 @@ test('component inheritAttrs false keeps host attrs off child root', () => { { components: { comp } }, { element: root, - template: - ' |
|---|