From 9b8d789da03ac1af61ee2151333d046a234e11ac Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Tue, 20 Jan 2026 21:02:58 -0800 Subject: [PATCH] Fix children not rendering when render() called from different module Create stable component function references at definition time instead of on every render. This fixes Hono's JSX reconciliation losing track of children when the component type identity keeps changing. Co-Authored-By: Claude Opus 4.5 --- src/index.tsx | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index a14ef6b..e47c885 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -147,8 +147,10 @@ function makeStyle(def: TagDef) { return style } +const ROOT_PROPS_KEY = '_forgeRootProps' + // turns a TagDef into a JSX component -function makeComponent(baseName: string, rootDef: TagDef, rootProps: Record, partName?: string) { +function makeComponent(baseName: string, rootDef: TagDef, partName?: string) { const def = partName ? rootDef.parts?.[partName]! : rootDef const base = def.base ?? 'div' @@ -167,7 +169,7 @@ function makeComponent(baseName: string, rootDef: TagDef, rootProps: Record { + return ({ [ROOT_PROPS_KEY]: rootProps = {}, children, ...props }: { [ROOT_PROPS_KEY]?: Record, children: any, [key: string]: any }) => { const classNames = [makeClassName(baseName, partName)] const allProps = { ...rootProps, ...props } @@ -286,14 +288,28 @@ export function define(nameOrDef: string | TagDef, defIfNamed?: TagDef) { if (styles[name]) throw `${name} is already defined! Must use unique names.` registerStyles(name, def) + // ensure component function identity doesn't change between renders + const components: Record any> = {} + + for (const [partName] of Object.entries(def.parts ?? {})) + components[partName] = makeComponent(name, def, partName) + + const RootComponent = makeComponent(name, def) + return (props: Record) => { - const parts: Record = {} + if (def.render) { + // For custom render, create parts object that injects _forgeRootProps + const parts: Record any> = {} - for (const [part] of Object.entries(def.parts ?? {})) - parts[part] = makeComponent(name, def, props, part) + for (const [partName, Comp] of Object.entries(components)) + parts[partName] = (partProps: any) => - parts.Root = makeComponent(name, def, props) - return def.render?.({ props, parts }) ?? {props.children} + parts.Root = (partProps: any) => + return def.render({ props, parts }) + } + + // Default render + return {props.children} } }