Repository navigation
Closed
Description
Problem
dynamicComponent is a renderer-agnostic primitive, but it lives in @solidjs/web. So universal and custom renderers can't use it without depending on web. Moving it is byte-neutral: the code changes packages and stays tree-shaken.
In both web entries, dynamic and dynamicComponent are thin wrappers over one dynamicCore(source, options, tagArm?):
- Client:
packages/web/src/index.ts.dynamic=dynamicCore(source, options, staticElement)(341–346).dynamicComponent=dynamicCore(source, options)(380–385).dynamicCoreis at 402–579 andstaticDynamicat 584–601. - Server:
packages/web/src/index.server.ts.dynamicpassesssrTag(116–121, 140–141),dynamicComponentpasses nothing (131–136), anddynamicCoreis at 145–277.
The tag arm is injected as a parameter on purpose, so a bundle that only uses dynamicComponent drops the element runtime. That is the only DOM- or SSR-specific part. Everything else depends on solid-js alone:
- Client core:
createMemo,createSignal,onCleanupanduntrack, plussharedConfigand$DEVCOMPfromsolid-js/internal. It also uses web'sisDevconstant, which maps to core'sIS_DEV. - Server core:
createMemo,untrack,NotReadyErrorandsharedConfig.context.block. ItsclassifyLanding(311–336) raisesDYNAMIC_ASYNC_COMPONENTthrough web'srecordFinding/IS_OBSERVE;packages/solid/src/server/diagnostics.tsalready exportsrecordFinding,IS_DEVandIS_OBSERVE. - Server-component markers: the binding marker is
Symbol.for("solid.component-binding")(index.ts:291, matchingframes/src/frame-transport.ts:373). The server-component brand isSymbol.for("solid.server-component")(index.server.ts:294). Neither needs an import from frames. FLIGHT: a module-localSymbol("solid.dynamic-flight").
So core needs no knowledge of the frames transport. It keeps duck-typing the same global symbols.
Proposal
- Move
dynamicCore(taking an optional tag arm),staticDynamic,bindingOf,FLIGHT,classifyLandingandDynamicOptionsintosolid-js. Add them to both the client build (src/client/) and the server build (src/server/), the same way aslazy. - Export
dynamicComponentfromsolid-js. - Export the tag-arm core from
solid-jsas@internal. Read it back throughsolid-js/internal, the same waycreateLoadingBoundary/createErrorBoundaryare today (packages/solid/src/internal.ts). @solidjs/webkeepsdynamic= core +staticElement(client) orssrTag(server), and keepsDynamic(built ondynamic, notdynamicComponent). It re-exportsdynamicComponentfromsolid-js.
What moves with it
- Types:
packages/web/types/index.d.ts:150andindex.server.d.ts:54currently disagree: the client source type acceptsAsyncIterable<C>, the server's doesn't.solid-jsships onetypes/index.d.tsfor every condition, so one signature has to be picked (presumably the client's).ComponentProps<C>forC extends Componentis the same in core and web. - Tests:
packages/web/test/dynamic-component.type-tests.tsx,server/dynamic-component-parity.spec.tsx,hydration/dynamic-component-parity-*.spec.tsx,harness/dynamic-component-parity.tsx,frames-errored-reset-refetch.spec.tsx, andconsistency/c16-*/c17-*. The parity tests checkdynamicanddynamicComponentagainst each other, so they probably stay in web and import both. - Docs and size scripts:
documentation/solid-2.0/03-control-flow.md,10-server-functions.md,11-server-components.md,documentation/server-components/*.md, and thedynamicComponentimports inscripts/size/fixtures and scenarios.
Cost
- Apps that don't use it pay nothing; it's tree-shaken like
lazy. - Apps that use it pay the same bytes as today — the code only changes packages.
Open questions
- Should the server
DYNAMIC_ASYNC_COMPONENTcheck stay in core as is? Its message refers todynamic(). - Should
dynamicmove too, with the element tag arm passed in? Probably not: the tag arm needs the element runtime, and that is the reason it lives in web.
Metadata
Metadata
Assignees
Labels
No labels
Activity
ryansolid commentedon Oct 8, 2026