@remix-run/ui 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/README.md +33 -10
  2. package/dist/animation/animate-layout-mixin.js +66 -66
  3. package/dist/animation/animate-layout-mixin.js.map +1 -1
  4. package/dist/animation/spring.d.ts +6 -35
  5. package/dist/animation/spring.js +9 -8
  6. package/dist/animation/spring.js.map +1 -1
  7. package/dist/components/accordion/accordion.d.ts +7 -7
  8. package/dist/components/accordion/accordion.js +0 -2
  9. package/dist/components/accordion/accordion.js.map +1 -1
  10. package/dist/components/accordion/demos/card.demo.d.ts +6 -0
  11. package/dist/components/accordion/demos/card.demo.js +58 -0
  12. package/dist/components/accordion/demos/card.demo.js.map +1 -0
  13. package/dist/components/accordion/demos/multiple.demo.d.ts +6 -0
  14. package/dist/components/accordion/demos/multiple.demo.js +19 -0
  15. package/dist/components/accordion/demos/multiple.demo.js.map +1 -0
  16. package/dist/components/accordion/demos/overview.demo.d.ts +6 -0
  17. package/dist/components/accordion/demos/overview.demo.js +19 -0
  18. package/dist/components/accordion/demos/overview.demo.js.map +1 -0
  19. package/dist/components/breadcrumbs/breadcrumbs.d.ts +1 -1
  20. package/dist/components/breadcrumbs/breadcrumbs.js +0 -2
  21. package/dist/components/breadcrumbs/breadcrumbs.js.map +1 -1
  22. package/dist/components/breadcrumbs/demos/basic.demo.d.ts +5 -0
  23. package/dist/components/breadcrumbs/demos/basic.demo.js +14 -0
  24. package/dist/components/breadcrumbs/demos/basic.demo.js.map +1 -0
  25. package/dist/components/breadcrumbs/demos/separator.demo.d.ts +5 -0
  26. package/dist/components/breadcrumbs/demos/separator.demo.js +14 -0
  27. package/dist/components/breadcrumbs/demos/separator.demo.js.map +1 -0
  28. package/dist/components/button/button.d.ts +86 -4
  29. package/dist/components/button/button.js +63 -3
  30. package/dist/components/button/button.js.map +1 -1
  31. package/dist/components/button/demos/aliases.demo.d.ts +6 -0
  32. package/dist/components/button/demos/aliases.demo.js +18 -0
  33. package/dist/components/button/demos/aliases.demo.js.map +1 -0
  34. package/dist/components/button/demos/basic.demo.d.ts +6 -0
  35. package/dist/components/button/demos/basic.demo.js +19 -0
  36. package/dist/components/button/demos/basic.demo.js.map +1 -0
  37. package/dist/components/button/demos/states.demo.d.ts +6 -0
  38. package/dist/components/button/demos/states.demo.js +29 -0
  39. package/dist/components/button/demos/states.demo.js.map +1 -0
  40. package/dist/components/combobox/combobox.d.ts +11 -11
  41. package/dist/components/combobox/combobox.js +0 -2
  42. package/dist/components/combobox/combobox.js.map +1 -1
  43. package/dist/components/combobox/demos/overview.demo.d.ts +6 -0
  44. package/dist/components/combobox/demos/overview.demo.js +153 -0
  45. package/dist/components/combobox/demos/overview.demo.js.map +1 -0
  46. package/dist/components/listbox/demos/overview.demo.d.ts +6 -0
  47. package/dist/components/listbox/demos/overview.demo.js +64 -0
  48. package/dist/components/listbox/demos/overview.demo.js.map +1 -0
  49. package/dist/components/listbox/listbox.d.ts +4 -9
  50. package/dist/components/listbox/listbox.js +21 -33
  51. package/dist/components/listbox/listbox.js.map +1 -1
  52. package/dist/components/menu/demos/bubbling.demo.d.ts +5 -0
  53. package/dist/components/menu/demos/bubbling.demo.js +15 -0
  54. package/dist/components/menu/demos/bubbling.demo.js.map +1 -0
  55. package/dist/components/menu/demos/overview.demo.d.ts +6 -0
  56. package/dist/components/menu/demos/overview.demo.js +32 -0
  57. package/dist/components/menu/demos/overview.demo.js.map +1 -0
  58. package/dist/components/menu/menu.d.ts +15 -24
  59. package/dist/components/menu/menu.js +23 -38
  60. package/dist/components/menu/menu.js.map +1 -1
  61. package/dist/components/popover/demos/overview.demo.d.ts +6 -0
  62. package/dist/components/popover/demos/overview.demo.js +64 -0
  63. package/dist/components/popover/demos/overview.demo.js.map +1 -0
  64. package/dist/components/popover/popover.d.ts +5 -5
  65. package/dist/components/popover/popover.js.map +1 -1
  66. package/dist/components/select/demos/deconstructed.demo.d.ts +6 -0
  67. package/dist/components/select/demos/deconstructed.demo.js +53 -0
  68. package/dist/components/select/demos/deconstructed.demo.js.map +1 -0
  69. package/dist/components/select/demos/overview.demo.d.ts +5 -0
  70. package/dist/components/select/demos/overview.demo.js +27 -0
  71. package/dist/components/select/demos/overview.demo.js.map +1 -0
  72. package/dist/components/select/select.d.ts +11 -11
  73. package/dist/components/select/select.js +17 -26
  74. package/dist/components/select/select.js.map +1 -1
  75. package/dist/components/tabs/tabs.d.ts +6 -6
  76. package/dist/components/tabs/tabs.js +0 -2
  77. package/dist/components/tabs/tabs.js.map +1 -1
  78. package/dist/index.d.ts +2 -2
  79. package/dist/index.js.map +1 -1
  80. package/dist/interactions/keydown/keydown.d.ts +1 -1
  81. package/dist/interactions/outside-click/outside-click-mixin.d.ts +2 -1
  82. package/dist/interactions/typeahead/typeahead-mixin.d.ts +3 -1
  83. package/dist/runtime/component.d.ts +18 -12
  84. package/dist/runtime/component.js +93 -80
  85. package/dist/runtime/component.js.map +1 -1
  86. package/dist/runtime/core/attributes.d.ts +14 -0
  87. package/dist/runtime/core/attributes.js +103 -0
  88. package/dist/runtime/core/attributes.js.map +1 -0
  89. package/dist/runtime/core/children.d.ts +7 -0
  90. package/dist/runtime/core/children.js +30 -0
  91. package/dist/runtime/core/children.js.map +1 -0
  92. package/dist/runtime/core/props.d.ts +9 -0
  93. package/dist/runtime/core/props.js +170 -0
  94. package/dist/runtime/core/props.js.map +1 -0
  95. package/dist/runtime/core/vnode.d.ts +3 -0
  96. package/dist/runtime/core/vnode.js +39 -0
  97. package/dist/runtime/core/vnode.js.map +1 -0
  98. package/dist/runtime/create-element.js +2 -1
  99. package/dist/runtime/create-element.js.map +1 -1
  100. package/dist/runtime/diff-dom.js +47 -14
  101. package/dist/runtime/diff-dom.js.map +1 -1
  102. package/dist/runtime/diff-props.d.ts +1 -2
  103. package/dist/runtime/diff-props.js +2 -181
  104. package/dist/runtime/diff-props.js.map +1 -1
  105. package/dist/runtime/dom.d.ts +20 -9
  106. package/dist/runtime/frame.d.ts +23 -2
  107. package/dist/runtime/frame.js +41 -47
  108. package/dist/runtime/frame.js.map +1 -1
  109. package/dist/runtime/jsx.d.ts +1 -1
  110. package/dist/runtime/jsx.js +2 -28
  111. package/dist/runtime/jsx.js.map +1 -1
  112. package/dist/runtime/mixins/link-mixin.d.ts +5 -1
  113. package/dist/runtime/mixins/link-mixin.js.map +1 -1
  114. package/dist/runtime/mixins/mixin.d.ts +2 -1
  115. package/dist/runtime/mixins/mixin.js +9 -4
  116. package/dist/runtime/mixins/mixin.js.map +1 -1
  117. package/dist/runtime/mixins/on-mixin.d.ts +15 -4
  118. package/dist/runtime/mixins/on-mixin.js +9 -2
  119. package/dist/runtime/mixins/on-mixin.js.map +1 -1
  120. package/dist/runtime/mixins/ref-mixin.d.ts +2 -1
  121. package/dist/runtime/mixins/ref-mixin.js.map +1 -1
  122. package/dist/runtime/navigation.js +9 -1
  123. package/dist/runtime/navigation.js.map +1 -1
  124. package/dist/runtime/reconcile.d.ts +1 -1
  125. package/dist/runtime/reconcile.js +452 -197
  126. package/dist/runtime/reconcile.js.map +1 -1
  127. package/dist/runtime/run.d.ts +17 -2
  128. package/dist/runtime/run.js.map +1 -1
  129. package/dist/runtime/scheduler.d.ts +10 -10
  130. package/dist/runtime/scheduler.js +1 -1
  131. package/dist/runtime/scheduler.js.map +1 -1
  132. package/dist/runtime/stream-protocol.d.ts +8 -0
  133. package/dist/runtime/stream-protocol.js +20 -0
  134. package/dist/runtime/stream-protocol.js.map +1 -0
  135. package/dist/runtime/svg-attributes.js +1 -0
  136. package/dist/runtime/svg-attributes.js.map +1 -1
  137. package/dist/runtime/to-vnode.js +7 -11
  138. package/dist/runtime/to-vnode.js.map +1 -1
  139. package/dist/runtime/vnode.d.ts +11 -5
  140. package/dist/runtime/vnode.js +6 -3
  141. package/dist/runtime/vnode.js.map +1 -1
  142. package/dist/server/stream.d.ts +2 -0
  143. package/dist/server/stream.js +154 -114
  144. package/dist/server/stream.js.map +1 -1
  145. package/dist/style/css-mixin.d.ts +2 -1
  146. package/dist/style/css-mixin.js.map +1 -1
  147. package/dist/style/index.d.ts +1 -1
  148. package/dist/style/layers.d.ts +2 -0
  149. package/dist/style/layers.js +3 -0
  150. package/dist/style/layers.js.map +1 -0
  151. package/dist/style/stylesheet.d.ts +13 -11
  152. package/dist/style/stylesheet.js +69 -8
  153. package/dist/style/stylesheet.js.map +1 -1
  154. package/dist/theme/contract.d.ts +24 -222
  155. package/dist/theme/contract.js +2 -2
  156. package/dist/theme/contract.js.map +1 -1
  157. package/dist/theme/glyph-contract.d.ts +1 -3
  158. package/dist/theme/glyph-contract.js.map +1 -1
  159. package/dist/theme/presets/rmx-01/index.d.ts +4 -2
  160. package/dist/theme/presets/rmx-01/index.js.map +1 -1
  161. package/dist/theme/runtime.js +4 -1
  162. package/dist/theme/runtime.js.map +1 -1
  163. package/dist/utils/scroll-lock.d.ts +3 -3
  164. package/dist/utils/scroll-lock.js.map +1 -1
  165. package/package.json +3 -3
  166. package/src/animation/README.md +316 -0
  167. package/src/animation/animate-layout-mixin.ts +79 -71
  168. package/src/animation/spring.ts +21 -9
  169. package/src/components/accordion/README.md +166 -0
  170. package/src/components/accordion/accordion.tsx +14 -14
  171. package/src/components/accordion/demos/card.demo.tsx +113 -0
  172. package/src/components/accordion/demos/multiple.demo.tsx +56 -0
  173. package/src/components/accordion/demos/overview.demo.tsx +56 -0
  174. package/src/components/anchor/README.md +153 -0
  175. package/src/components/anchor/anchor.ts +1 -1
  176. package/src/components/breadcrumbs/README.md +55 -0
  177. package/src/components/breadcrumbs/breadcrumbs.tsx +1 -3
  178. package/src/components/breadcrumbs/demos/basic.demo.tsx +17 -0
  179. package/src/components/breadcrumbs/demos/separator.demo.tsx +18 -0
  180. package/src/components/button/README.md +4 -4
  181. package/src/components/button/button.tsx +118 -20
  182. package/src/components/button/demos/aliases.demo.tsx +27 -0
  183. package/src/components/button/demos/basic.demo.tsx +30 -0
  184. package/src/components/button/demos/states.demo.tsx +52 -0
  185. package/src/components/combobox/README.md +1 -1
  186. package/src/components/combobox/combobox.tsx +37 -21
  187. package/src/components/combobox/demos/overview.demo.tsx +199 -0
  188. package/src/components/glyph/README.md +72 -0
  189. package/src/components/glyph/glyph.tsx +2 -2
  190. package/src/components/listbox/README.md +115 -0
  191. package/src/components/listbox/demos/overview.demo.tsx +101 -0
  192. package/src/components/listbox/listbox.ts +106 -110
  193. package/src/components/menu/README.md +96 -0
  194. package/src/components/menu/demos/bubbling.demo.tsx +34 -0
  195. package/src/components/menu/demos/overview.demo.tsx +76 -0
  196. package/src/components/menu/menu.tsx +61 -55
  197. package/src/components/popover/README.md +3 -3
  198. package/src/components/popover/demos/overview.demo.tsx +143 -0
  199. package/src/components/popover/popover.ts +90 -79
  200. package/src/components/select/README.md +107 -0
  201. package/src/components/select/demos/deconstructed.demo.tsx +95 -0
  202. package/src/components/select/demos/overview.demo.tsx +51 -0
  203. package/src/components/select/select.tsx +53 -36
  204. package/src/components/tabs/tabs.tsx +10 -5
  205. package/src/index.ts +2 -1
  206. package/src/runtime/component.ts +122 -88
  207. package/src/runtime/core/attributes.ts +118 -0
  208. package/src/runtime/core/children.ts +42 -0
  209. package/src/runtime/core/props.ts +187 -0
  210. package/src/runtime/core/vnode.ts +48 -0
  211. package/src/runtime/create-element.ts +2 -1
  212. package/src/runtime/diff-dom.ts +59 -12
  213. package/src/runtime/diff-props.ts +2 -187
  214. package/src/runtime/dom.ts +20 -9
  215. package/src/runtime/frame.ts +71 -55
  216. package/src/runtime/jsx.ts +3 -30
  217. package/src/runtime/mixins/link-mixin.ts +88 -86
  218. package/src/runtime/mixins/mixin.ts +18 -5
  219. package/src/runtime/mixins/on-mixin.ts +20 -12
  220. package/src/runtime/mixins/ref-mixin.ts +4 -5
  221. package/src/runtime/navigation.ts +10 -1
  222. package/src/runtime/reconcile.ts +563 -223
  223. package/src/runtime/run.ts +17 -1
  224. package/src/runtime/scheduler.ts +20 -20
  225. package/src/runtime/stream-protocol.ts +27 -0
  226. package/src/runtime/svg-attributes.ts +1 -0
  227. package/src/runtime/to-vnode.ts +7 -10
  228. package/src/runtime/vnode.ts +16 -9
  229. package/src/server/README.md +90 -0
  230. package/src/server/stream.ts +176 -124
  231. package/src/style/css-mixin.ts +6 -1
  232. package/src/style/index.ts +1 -2
  233. package/src/style/layers.ts +2 -0
  234. package/src/style/stylesheet.ts +88 -11
  235. package/src/test/README.md +107 -0
  236. package/src/test/utils.ts +3 -1
  237. package/src/theme/README.md +103 -0
  238. package/src/theme/contract.ts +37 -5
  239. package/src/theme/glyph-contract.ts +3 -1
  240. package/src/theme/presets/rmx-01/index.ts +4 -2
  241. package/src/theme/runtime.ts +4 -1
  242. package/src/utils/scroll-lock/README.md +33 -0
  243. package/src/utils/scroll-lock.ts +7 -3
@@ -11,8 +11,21 @@ import { TypedEventTarget } from './typed-event-target.ts'
11
11
  /**
12
12
  * Options for starting the client runtime with {@link run}.
13
13
  */
14
- export type RunInit = {
14
+ export interface RunInit {
15
+ /**
16
+ * Loads the named browser module export for a hydrated `clientEntry()`.
17
+ *
18
+ * Implementations usually call dynamic `import(moduleUrl)` and return
19
+ * `mod[exportName]`.
20
+ */
15
21
  loadModule: LoadModule
22
+
23
+ /**
24
+ * Resolves browser-loaded `<Frame>` content.
25
+ *
26
+ * Omit this only when the runtime never needs to load or reload frames in the
27
+ * browser.
28
+ */
16
29
  resolveFrame?: ResolveFrame
17
30
  }
18
31
 
@@ -27,8 +40,11 @@ export type AppRuntimeEventMap = {
27
40
  * Client runtime returned by {@link run}.
28
41
  */
29
42
  export type AppRuntime = TypedEventTarget<AppRuntimeEventMap> & {
43
+ /** Resolves after the current document finishes hydrating. */
30
44
  ready(): Promise<void>
45
+ /** Flushes any queued component updates synchronously. */
31
46
  flush(): void
47
+ /** Stops runtime listeners and disposes the top-level frame. */
32
48
  dispose(): void
33
49
  }
34
50
 
@@ -17,7 +17,23 @@ type SchedulerPhaseListener = EventListenerOrEventListenerObject | null
17
17
  /**
18
18
  * Scheduler API used by the reconciler and frame runtime.
19
19
  */
20
- export type Scheduler = ReturnType<typeof createScheduler>
20
+ export interface Scheduler {
21
+ enqueue(vnode: CommittedComponentNode, domParent: ParentNode): void
22
+ enqueueWork(newTasks: EmptyFn[]): void
23
+ enqueueCommitPhase(newTasks: EmptyFn[]): void
24
+ enqueueTasks(newTasks: EmptyFn[]): void
25
+ addEventListener(
26
+ type: SchedulerPhaseType,
27
+ listener: SchedulerPhaseListener,
28
+ options?: AddEventListenerOptions | boolean,
29
+ ): void
30
+ removeEventListener(
31
+ type: SchedulerPhaseType,
32
+ listener: SchedulerPhaseListener,
33
+ options?: EventListenerOptions | boolean,
34
+ ): void
35
+ dequeue(): void
36
+ }
21
37
 
22
38
  // Protect against infinite cascading updates (e.g. handle.update() during render)
23
39
  const MAX_CASCADING_UPDATES = 50
@@ -38,7 +54,7 @@ export function createScheduler(
38
54
  doc: Document,
39
55
  rootTarget: EventTarget,
40
56
  styles: StyleManager = defaultStyleManager,
41
- ) {
57
+ ): Scheduler {
42
58
  let documentState = createDocumentState(doc)
43
59
  let scheduled = new Map<CommittedComponentNode, ParentNode>()
44
60
  let workTasks: EmptyFn[] = []
@@ -53,23 +69,7 @@ export function createScheduler(
53
69
  beforeUpdate: 0,
54
70
  commit: 0,
55
71
  }
56
- let scheduler: {
57
- enqueue(vnode: CommittedComponentNode, domParent: ParentNode): void
58
- enqueueWork(newTasks: EmptyFn[]): void
59
- enqueueCommitPhase(newTasks: EmptyFn[]): void
60
- enqueueTasks(newTasks: EmptyFn[]): void
61
- addEventListener(
62
- type: SchedulerPhaseType,
63
- listener: SchedulerPhaseListener,
64
- options?: AddEventListenerOptions | boolean,
65
- ): void
66
- removeEventListener(
67
- type: SchedulerPhaseType,
68
- listener: SchedulerPhaseListener,
69
- options?: EventListenerOptions | boolean,
70
- ): void
71
- dequeue(): void
72
- }
72
+ let scheduler: Scheduler
73
73
 
74
74
  function dispatchError(error: unknown) {
75
75
  console.error(error)
@@ -137,7 +137,7 @@ export function createScheduler(
137
137
  // Needed for fragment self-updates that add children - without this, new children
138
138
  // would be appended after siblings. The keyed diff has placement logic, but unkeyed
139
139
  // diff relies on anchor for correct positioning.
140
- let anchor = findNextSiblingDomAnchor(vnode, vParent) || undefined
140
+ let anchor = findNextSiblingDomAnchor(vnode) || undefined
141
141
  try {
142
142
  let updateStyles = getFrameStyleManager(vnode)
143
143
  renderComponent(
@@ -0,0 +1,27 @@
1
+ export type FlushKind = 'document' | 'fragment'
2
+
3
+ const FLUSH_MARKER_PATTERN = /<!--\s*rmx:flush\s+(document|fragment)\s*-->/g
4
+
5
+ export function appendFlushMarker(html: string, kind: FlushKind): string {
6
+ return `${html}<!-- rmx:flush ${kind} -->`
7
+ }
8
+
9
+ export function stripFlushMarkers(html: string): string {
10
+ FLUSH_MARKER_PATTERN.lastIndex = 0
11
+ return html.replace(FLUSH_MARKER_PATTERN, '')
12
+ }
13
+
14
+ export function findFlushMarker(
15
+ html: string,
16
+ startIndex: number,
17
+ ): { index: number; endIndex: number; kind: FlushKind } | undefined {
18
+ FLUSH_MARKER_PATTERN.lastIndex = startIndex
19
+ let match = FLUSH_MARKER_PATTERN.exec(html)
20
+ if (!match) return undefined
21
+
22
+ return {
23
+ index: match.index,
24
+ endIndex: FLUSH_MARKER_PATTERN.lastIndex,
25
+ kind: match[1] as FlushKind,
26
+ }
27
+ }
@@ -5,6 +5,7 @@ const CANONICAL_CAMEL_SVG_ATTRS = new Set([
5
5
  'accentHeight',
6
6
  'attributeName',
7
7
  'attributeType',
8
+ 'autoReverse',
8
9
  'baseFrequency',
9
10
  'baseProfile',
10
11
  'calcMode',
@@ -1,7 +1,8 @@
1
1
  import { Fragment } from './component.ts'
2
2
  import { invariant } from './invariant.ts'
3
+ import { isEmptyChild, isPrimitiveChild, normalizeChildren } from './core/children.ts'
3
4
  import type { RemixElement, RemixNode } from './jsx.ts'
4
- import { isRemixElement, TEXT_NODE, type VNode } from './vnode.ts'
5
+ import { isRemixElement, NON_RENDER_NODE, TEXT_NODE, type VNode } from './vnode.ts'
5
6
 
6
7
  function flatMapChildrenToVNodes(node: RemixElement): VNode[] {
7
8
  if (!('children' in node.props)) return []
@@ -13,21 +14,17 @@ function flatMapChildrenToVNodes(node: RemixElement): VNode[] {
13
14
  }
14
15
 
15
16
  function flattenChildrenToVNodes(nodes: RemixNode[], out: VNode[]): void {
16
- for (let child of nodes) {
17
- if (Array.isArray(child)) {
18
- flattenChildrenToVNodes(child, out)
19
- } else {
20
- out.push(toVNode(child))
21
- }
17
+ for (let child of normalizeChildren(nodes)) {
18
+ out.push(toVNode(child))
22
19
  }
23
20
  }
24
21
 
25
22
  export function toVNode(node: RemixNode): VNode {
26
- if (node === null || node === undefined || typeof node === 'boolean') {
27
- return { type: TEXT_NODE, _text: '' }
23
+ if (isEmptyChild(node)) {
24
+ return { type: NON_RENDER_NODE }
28
25
  }
29
26
 
30
- if (typeof node === 'string' || typeof node === 'number' || typeof node === 'bigint') {
27
+ if (isPrimitiveChild(node)) {
31
28
  return { type: TEXT_NODE, _text: String(node) }
32
29
  }
33
30
 
@@ -1,8 +1,12 @@
1
1
  import type { ComponentHandle, Component } from './component.ts'
2
2
  import { Fragment, Frame } from './component.ts'
3
- import type { ElementProps, RemixElement, RemixNode } from './jsx.ts'
3
+ import { isRemixElement } from './core/vnode.ts'
4
+ import type { ElementProps, RemixNode } from './jsx.ts'
5
+
6
+ export { isRemixElement }
4
7
 
5
8
  export const TEXT_NODE = Symbol('TEXT_NODE')
9
+ export const NON_RENDER_NODE = Symbol('NON_RENDER_NODE')
6
10
  export const ROOT_VNODE = Symbol('ROOT_VNODE')
7
11
 
8
12
  export type VNodeType =
@@ -10,6 +14,7 @@ export type VNodeType =
10
14
  | string // host element
11
15
  | Function // component
12
16
  | typeof TEXT_NODE
17
+ | typeof NON_RENDER_NODE
13
18
  | typeof Fragment
14
19
  | typeof Frame
15
20
 
@@ -25,6 +30,8 @@ export type VNode<T extends VNodeType = VNodeType> = {
25
30
  _dom?: unknown
26
31
  _controller?: AbortController
27
32
  _mixState?: unknown
33
+ _directEventDescriptors?: unknown
34
+ _directEventState?: unknown
28
35
  _controlledState?: unknown
29
36
  _svg?: boolean
30
37
  // Range roots render between comment boundary markers
@@ -37,10 +44,6 @@ export type VNode<T extends VNodeType = VNodeType> = {
37
44
  _frameResolveController?: AbortController
38
45
  _frameResolved?: boolean
39
46
 
40
- // Internal diffing fields
41
- _index?: number
42
- _flags?: number
43
-
44
47
  // TEXT_NODE
45
48
  _text?: string
46
49
 
@@ -65,6 +68,10 @@ export type TextNode = VNode & {
65
68
  _text: string
66
69
  }
67
70
 
71
+ export type NonRenderNode = VNode & {
72
+ type: typeof NON_RENDER_NODE
73
+ }
74
+
68
75
  export type CommittedTextNode = TextNode & {
69
76
  _dom: Text
70
77
  }
@@ -101,6 +108,10 @@ export function isTextNode(node: VNode): node is TextNode {
101
108
  return node.type === TEXT_NODE
102
109
  }
103
110
 
111
+ export function isNonRenderNode(node: VNode): node is NonRenderNode {
112
+ return node.type === NON_RENDER_NODE
113
+ }
114
+
104
115
  export function isCommittedTextNode(node: VNode): node is CommittedTextNode {
105
116
  return isTextNode(node) && node._dom instanceof Text
106
117
  }
@@ -121,10 +132,6 @@ export function isCommittedComponentNode(node: VNode): node is CommittedComponen
121
132
  return isComponentNode(node) && node._content !== undefined
122
133
  }
123
134
 
124
- export function isRemixElement(node: RemixNode): node is RemixElement {
125
- return typeof node === 'object' && node !== null && '$rmx' in node
126
- }
127
-
128
135
  export function findContextFromAncestry(node: VNode, type: Component): unknown {
129
136
  let current: VNode | undefined = node
130
137
  while (current) {
@@ -0,0 +1,90 @@
1
+ # Server
2
+
3
+ Remix UI can render to HTML on the server using two APIs:
4
+
5
+ - `renderToString` - Returns a complete HTML string. Simple, but buffers the entire response.
6
+ - `renderToStream` - Returns a `ReadableStream<Uint8Array>`. Sends the initial HTML immediately and streams frame content as it resolves.
7
+
8
+ Both are exported from `remix/ui/server`.
9
+
10
+ ## renderToString
11
+
12
+ Renders a component tree to a complete HTML string. Use this when you need the full output before responding (e.g., generating static pages or embedding HTML in an email).
13
+
14
+ ```tsx
15
+ import { renderToString } from 'remix/ui/server'
16
+
17
+ let html = await renderToString(<App />)
18
+ ```
19
+
20
+ ## renderToStream
21
+
22
+ Renders a component tree to a streaming response. The initial HTML is sent immediately. Any `<Frame>` components with a `fallback` prop will render the fallback first, then stream the resolved content as it becomes available.
23
+
24
+ ```tsx
25
+ import { renderToStream } from 'remix/ui/server'
26
+
27
+ let stream = renderToStream(<App />, {
28
+ frameSrc: request.url,
29
+ signal: request.signal,
30
+ resolveFrame(src, _target, context) {
31
+ let frameUrl = new URL(src, context?.currentFrameSrc ?? request.url)
32
+ return fetchHtml(frameUrl)
33
+ },
34
+ onError(error) {
35
+ console.error(error)
36
+ },
37
+ })
38
+
39
+ return new Response(stream, {
40
+ headers: { 'Content-Type': 'text/html; charset=utf-8' },
41
+ })
42
+ ```
43
+
44
+ ### Options
45
+
46
+ - **`frameSrc`** - Seeds SSR frame state for the current render. When provided, server-rendered components can read `handle.frame.src` and `handle.frames.top.src` during SSR.
47
+ - **`topFrameSrc`** - Overrides the root frame URL used for `handle.frames.top.src`. This is mainly useful when calling `renderToStream()` from inside `resolveFrame()` for a nested frame render.
48
+ - **`signal`** - Cancels pending server rendering work. Pass `request.signal` so client disconnects can stop unresolved frame work without invoking `onError` for the disconnect itself.
49
+ - **`resolveFrame(src, target, context)`** - Called when a `<Frame>` needs its content. Return a string of HTML, a `ReadableStream<Uint8Array>`, or a promise of either. `context.currentFrameSrc` is the URL for the frame that contains the `<Frame>`, and `context.topFrameSrc` is the outer document URL. Required if your component tree contains `<Frame>` elements.
50
+ - **`onError(error)`** - Called when a rendering error occurs. If not provided, the stream rejects with the error.
51
+
52
+ When you render nested frame responses with `renderToStream()` inside `resolveFrame()`, pass `frameSrc` for the frame being rendered and carry `topFrameSrc` forward from the parent context. That preserves `handle.frames.top.src` across the whole SSR frame tree.
53
+
54
+ ### Streaming behavior
55
+
56
+ When the stream encounters a `<Frame>` component:
57
+
58
+ - **Without `fallback`** (blocking): The frame content is awaited before the initial HTML chunk is sent. The resolved content appears inline.
59
+ - **With `fallback`** (non-blocking): The fallback is rendered inline in the initial chunk. Once the frame resolves, a `<template>` element containing the real content is streamed at the end of the response. The client swaps it in automatically.
60
+
61
+ This means the first chunk always contains a complete, renderable page. Slow data sources don't block the initial paint.
62
+
63
+ ## Head content
64
+
65
+ To render content into the document head during SSR, use an explicit `<head>` element:
66
+
67
+ ```tsx
68
+ function ProductPage() {
69
+ return () => (
70
+ <html>
71
+ <head>
72
+ <title>Product Name</title>
73
+ <meta name="description" content="A great product" />
74
+ </head>
75
+ <body>
76
+ <h1>Product Name</h1>
77
+ </body>
78
+ </html>
79
+ )
80
+ }
81
+ ```
82
+
83
+ ## CSS
84
+
85
+ Components using the `css(...)` mixin through `mix` have their styles collected during rendering and emitted as a single `<style>` tag in the `<head>`. No client-side style injection is needed for server-rendered content.
86
+
87
+ ## See Also
88
+
89
+ - [Hydration](https://github.com/remix-run/remix/blob/main/packages/ui/docs/hydration.md) - Making server-rendered components interactive on the client
90
+ - [Frames](https://github.com/remix-run/remix/blob/main/packages/ui/docs/frames.md) - Streaming partial server UI with `<Frame>`