Repository navigation
Conversation
clientOnly only accepted a module with a default export, so a named
export needed import(...).then(m => ({ default: m.Chart })). A loader can
now resolve to the component itself, which also works for named exports:
clientOnly(() => import("./chart").then(({ Chart }) => Chart))
Destructuring the export inside the loader lets the bundler leave the
module's other exports out of the chunk. On Vite 6, which 1.x uses, the
.then(m => m.Chart) form keeps every export, because Vite's preload
wrapper ends up between import() and .then.
Closes #2064.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: ac130a5 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
brenelz
approved these changes
Oct 14, 2026
Member
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2064 on the 1.x line. Same change as #2346 on
main.clientOnlyonly accepted a module with a default export, so a named export neededimport("./chart").then(m => ({ default: m.Chart })). A loader can now resolve to the component itself:Default exports work as before:
clientOnly(() => import("./map")).Change
() => Promise<T | { default: T }>.load()uses the result directly when it is a function, and itsdefaultotherwise. A module namespace object is never a function and a component always is, so the two cannot be confused.{ default }form, the explicitclientOnly<typeof X>(...)generic, and the{ default: m.Chart }workaround keep working.Tree-shaking on 1.x
#2346 explains why this is a loader form rather than an
exportoption: an option read with a computed key insideclientOnlykeeps every export of the module. On 1.x there is one more caveat. In theapps/testsproduction build (Vite 6.3.6, Rollup 4.52):.then(({ X }) => X)async () => { const { X } = await import(...); return X; }async () => (await import(...)).X.then(m => m.X)Vite 6 compiles
import(...).then(m => m.X)topreload(() => import(...), deps).then(m => m.X), so the.thensits outside the preload wrapper and Rollup cannot see which export it reads. A standalone Vite 6 library build, which has no preload wrapper, does tree-shake that form, so the cause is the wrapper and not Rollup. Vite 8 onmainputs the whole chain inside the wrapper and tree-shakes every form. So the docs, the changeset and the tests use the destructured form, which tree-shakes on both lines.Testing
apps/tests:/client-only/namedroute andcypress/e2e/client-only.cy.ts. The test checks that the server HTML has the fallback and not the component, and that the component renders after hydration. It fails without this change, because the component never renders. The default-export route is already covered byhydration.cy.ts.treeshaking/client-only.test.ts: the named export's code is in the client bundle, and the module's unused export is not.tsc5.9.2 against a temporary file: props are inferred for the default,.then(m => m.X), destructured andasyncforms.m.Missingis an error, and so are wrong or missing props. The explicit generic and the{ default }workaround compile.@solidjs/starttest:ci(95 tests),typecheckandbuildpass.apps/testsunit:ci(2) and the full Cypress suite against the production build (20) pass.🤖 Generated with Claude Code